File: CodeActions\ImplementInterface\ImplementExplicitlyTests.cs
Web Access
Project: src\src\EditorFeatures\CSharpTest\Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.ImplementInterface;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ImplementInterface;
 
[Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)]
public sealed class ImplementExplicitlyTests : AbstractCSharpCodeActionTest
{
    private const int SingleMember = 0;
    private const int SameInterface = 1;
    private const int AllInterfaces = 2;
 
    protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorTestWorkspace workspace, TestParameters parameters)
        => new CSharpImplementExplicitlyCodeRefactoringProvider();
 
    protected override ImmutableArray<CodeAction> MassageActions(ImmutableArray<CodeAction> actions)
        => FlattenActions(actions);
 
    [Fact]
    public async Task TestSingleMember()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IGoo { void Goo1(); void Goo2(); }
            interface IBar { void Bar(); }
 
            class C : IGoo, IBar
            {
                public void [||]Goo1() { }
 
                public void Goo2() { }
 
                public void Bar() { }
            }
            """,
            """
            interface IGoo { void Goo1(); void Goo2(); }
            interface IBar { void Bar(); }
 
            class C : IGoo, IBar
            {
                void IGoo.Goo1() { }
 
                public void Goo2() { }
 
                public void Bar() { }
            }
            """, index: SingleMember);
    }
 
    [Fact]
    public async Task TestSameInterface()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IGoo { void Goo1(); void Goo2(); }
            interface IBar { void Bar(); }
 
            class C : IGoo, IBar
            {
                public void [||]Goo1() { }
 
                public void Goo2() { }
 
                public void Bar() { }
            }
            """,
            """
            interface IGoo { void Goo1(); void Goo2(); }
            interface IBar { void Bar(); }
 
            class C : IGoo, IBar
            {
                void IGoo.Goo1() { }
 
                void IGoo.Goo2() { }
 
                public void Bar() { }
            }
            """, index: SameInterface);
    }
 
    [Fact]
    public async Task TestAllInterfaces()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IGoo { void Goo1(); void Goo2(); }
            interface IBar { void Bar(); }
 
            class C : IGoo, IBar
            {
                public void [||]Goo1() { }
 
                public void Goo2() { }
 
                public void Bar() { }
            }
            """,
            """
            interface IGoo { void Goo1(); void Goo2(); }
            interface IBar { void Bar(); }
 
            class C : IGoo, IBar
            {
                void IGoo.Goo1() { }
 
                void IGoo.Goo2() { }
 
                void IBar.Bar() { }
            }
            """, index: AllInterfaces);
    }
 
    [Fact]
    public async Task TestProperty()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IGoo { int Goo1 { get; } }
 
            class C : IGoo
            {
                public int [||]Goo1 { get { } }
            }
            """,
            """
            interface IGoo { int Goo1 { get; } }
 
            class C : IGoo
            {
                int IGoo.Goo1 { get { } }
            }
            """, index: SingleMember);
    }
 
    [Fact]
    public async Task TestEvent()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IGoo { event Action E; }
 
            class C : IGoo
            {
                public event Action [||]E { add { } remove { } }
            }
            """,
            """
            interface IGoo { event Action E; }
 
            class C : IGoo
            {
                event Action IGoo.E { add { } remove { } }
            }
            """, index: SingleMember);
    }
 
    [Fact]
    public async Task TestNotOnExplicitMember()
    {
        await TestMissingAsync(
            """
            interface IGoo { void Goo1(); }
 
            class C : IGoo
            {
                void IGoo.[||]Goo1() { }
            }
            """);
    }
 
    [Fact]
    public async Task TestNotOnUnboundImplicitImpl()
    {
        await TestMissingAsync(
            """
            interface IGoo { void Goo1(); }
 
            class C
            {
                public void [||]Goo1() { }
            }
            """);
    }
 
    [Fact]
    public async Task TestUpdateReferences_InsideDeclarations_Explicit()
    {
        await TestInRegularAndScriptAsync(
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                public int [||]Prop { get { return this.Prop; } set { this.Prop = value; } }
                public int M(int i) { return this.M(i); }
                public event Action Ev { add { this.Ev += value; } remove { this.Ev -= value; } }
            }
            """,
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                int IGoo.Prop { get { return ((IGoo)this).Prop; } set { ((IGoo)this).Prop = value; } }
                int IGoo.M(int i) { return ((IGoo)this).M(i); }
                event Action IGoo.Ev { add { ((IGoo)this).Ev += value; } remove { ((IGoo)this).Ev -= value; } }
            }
            """, index: SameInterface);
    }
 
    [Fact]
    public async Task TestUpdateReferences_InsideDeclarations_Implicit()
    {
        await TestInRegularAndScriptAsync(
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                public int [||]Prop { get { return Prop; } set { Prop = value; } }
                public int M(int i) { return M(i); }
                public event Action Ev { add { Ev += value; } remove { Ev -= value; } }
            }
            """,
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                int IGoo.Prop { get { return ((IGoo)this).Prop; } set { ((IGoo)this).Prop = value; } }
                int IGoo.M(int i) { return ((IGoo)this).M(i); }
                event Action IGoo.Ev { add { ((IGoo)this).Ev += value; } remove { ((IGoo)this).Ev -= value; } }
            }
            """, index: SameInterface);
    }
 
    [Fact]
    public async Task TestUpdateReferences_InternalImplicit()
    {
        await TestInRegularAndScriptAsync(
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                public int [||]Prop { get { } set { } }
                public int M(int i) { }
                public event Action Ev { add { } remove { } }
 
                void InternalImplicit()
                {
                    var v = Prop;
                    Prop = 1;
                    Prop++;
                    ++Prop;
 
                    M(0);
                    M(M(0));
 
                    Ev += () => {};
 
                    var v1 = nameof(Prop);
                }
            }
            """,
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                int IGoo.Prop { get { } set { } }
                int IGoo.M(int i) { }
                event Action IGoo.Ev { add { } remove { } }
 
                void InternalImplicit()
                {
                    var v = ((IGoo)this).Prop;
                    ((IGoo)this).Prop = 1;
                    ((IGoo)this).Prop++;
                    ++((IGoo)this).Prop;
 
                    ((IGoo)this).M(0);
                    ((IGoo)this).M(((IGoo)this).M(0));
 
                    ((IGoo)this).Ev += () => {};
 
                    var v1 = nameof(((IGoo)this).Prop);
                }
            }
            """, index: SameInterface);
    }
 
    [Fact]
    public async Task TestUpdateReferences_InternalExplicit()
    {
        await TestInRegularAndScriptAsync(
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                public int [||]Prop { get { } set { } }
                public int M(int i) { }
                public event Action Ev { add { } remove { } }
 
                void InternalExplicit()
                {
                    var v = this.Prop;
                    this.Prop = 1;
                    this.Prop++;
                    ++this.Prop;
 
                    this.M(0);
                    this.M(this.M(0));
 
                    this.Ev += () => {};
 
                    var v1 = nameof(this.Prop);
                }
            }
            """,
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                int IGoo.Prop { get { } set { } }
                int IGoo.M(int i) { }
                event Action IGoo.Ev { add { } remove { } }
 
                void InternalExplicit()
                {
                    var v = ((IGoo)this).Prop;
                    ((IGoo)this).Prop = 1;
                    ((IGoo)this).Prop++;
                    ++((IGoo)this).Prop;
 
                    ((IGoo)this).M(0);
                    ((IGoo)this).M(((IGoo)this).M(0));
 
                    ((IGoo)this).Ev += () => {};
 
                    var v1 = nameof(((IGoo)this).Prop);
                }
            }
            """, index: SameInterface);
    }
 
    [Fact]
    public async Task TestUpdateReferences_External()
    {
        await TestInRegularAndScriptAsync(
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                public int [||]Prop { get { } set { } }
                public int M(int i) { }
                public event Action Ev { add { } remove { } }
            }
 
            class T
            {
                void External(C c)
                {
                    var v = c.Prop;
                    c.Prop = 1;
                    c.Prop++;
                    ++c.Prop;
 
                    c.M(0);
                    c.M(c.M(0));
 
                    c.Ev += () => {};
 
                    new C
                    {
                        Prop = 1
                    };
 
                    var v1 = nameof(c.Prop);
                }
            }
            """,
            """
            using System;
            interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            class C : IGoo
            {
                int IGoo.Prop { get { } set { } }
                int IGoo.M(int i) { }
                event Action IGoo.Ev { add { } remove { } }
            }
 
            class T
            {
                void External(C c)
                {
                    var v = ((IGoo)c).Prop;
                    ((IGoo)c).Prop = 1;
                    ((IGoo)c).Prop++;
                    ++((IGoo)c).Prop;
 
                    ((IGoo)c).M(0);
                    ((IGoo)c).M(((IGoo)c).M(0));
 
                    ((IGoo)c).Ev += () => {};
 
                    new C
                    {
                        Prop = 1
                    };
 
                    var v1 = nameof(((IGoo)c).Prop);
                }
            }
            """, index: SameInterface);
    }
 
    [Fact]
    public async Task TestUpdateReferences_CrossLanguage()
    {
        await TestInRegularAndScriptAsync(
            """
            <Workspace>
                <Project Language="C#" CommonReferences="true" AssemblyName="A1">
                    <Document FilePath="File.cs">
            using System;
            public interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            public class C : IGoo
            {
                public int [||]Prop { get { } set { } }
                public int M(int i) { }
                public event Action Ev { add { } remove { } }
            }
                    </Document>
                </Project>
                <Project Language="Visual Basic" CommonReferences="true" AssemblyName="A2">
                    <ProjectReference>A1</ProjectReference>
                    <Document>
            class T
                sub External(c1 as C)
                    dim v = c1.Prop
                    c1.Prop = 1
 
                    c1.M(0)
                    c1.M(c1.M(0))
 
                    dim x = new C() with {
                        .Prop = 1
                    }
 
                    dim v1 = nameof(c1.Prop)
                end sub
            end class
                    </Document>
                </Project>
            </Workspace>
            """,
            """
            <Workspace>
                <Project Language="C#" CommonReferences="true" AssemblyName="A1">
                    <Document FilePath="File.cs">
            using System;
            public interface IGoo { int Prop { get; set; } int M(int i); event Action Ev; }
 
            public class C : IGoo
            {
                int IGoo.Prop { get { } set { } }
                int IGoo.M(int i) { }
                event Action IGoo.Ev { add { } remove { } }
            }
                    </Document>
                </Project>
                <Project Language="Visual Basic" CommonReferences="true" AssemblyName="A2">
                    <ProjectReference>A1</ProjectReference>
                    <Document>
            class T
                sub External(c1 as C)
                    dim v = DirectCast(c1, IGoo).Prop
                    DirectCast(c1, IGoo).Prop = 1
 
                    DirectCast(c1, IGoo).M(0)
                    DirectCast(c1, IGoo).M(DirectCast(c1, IGoo).M(0))
 
                    dim x = new C() with {
                        .Prop = 1
                    }
 
                    dim v1 = nameof(c1.Prop)
                end sub
            end class
                    </Document>
                </Project>
            </Workspace>
            """, index: SameInterface);
    }
 
    [Fact]
    public async Task TestMemberWhichImplementsMultipleMembers()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IGoo { int M(int i); }
            interface IBar { int M(int i); }
 
            class C : IGoo, IBar
            {
                public int [||]M(int i)
                {
                    throw new System.Exception();
                }
            }
            """,
            """
            interface IGoo { int M(int i); }
            interface IBar { int M(int i); }
 
            class C : IGoo, IBar
            {
                int IGoo.M(int i)
                {
                    throw new System.Exception();
                }
                int IBar.M(int i)
                {
                    throw new System.Exception();
                }
            }
            """, index: SingleMember);
    }
 
    [Fact]
    public async Task TestMemberWhichImplementsMultipleMembers2()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IGoo { int M(int i); }
            interface IBar { int M(int i); }
 
            class C : IGoo, IBar
            {
                public int [||]M(int i)
                {
                    return this.M(1);
                }
            }
            """,
            """
            interface IGoo { int M(int i); }
            interface IBar { int M(int i); }
 
            class C : IGoo, IBar
            {
                int IGoo.M(int i)
                {
                    return ((IGoo)this).M(1);
                }
                int IBar.M(int i)
                {
                    return ((IGoo)this).M(1);
                }
            }
            """, index: SingleMember);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52020")]
    public async Task TestWithContraints()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IRepro
            {
                void A<T>(int value) where T : class;
            }
 
            class Repro : IRepro
            {
                public void [||]A<T>(int value) where T : class
                {
                }
            }
            """,
            """
            interface IRepro
            {
                void A<T>(int value) where T : class;
            }
 
            class Repro : IRepro
            {
                void IRepro.A<T>(int value)
                {
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52020")]
    public async Task TestWithDefaultParameterValues()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IRepro
            {
                void A(int value = 0);
            }
 
            class Repro : IRepro
            {
                public void [||]A(int value = 0)
                {
                }
            }
            """,
            """
            interface IRepro
            {
                void A(int value = 0);
            }
 
            class Repro : IRepro
            {
                void IRepro.A(int value)
                {
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52020")]
    public async Task TestWithMismatchedDefaultParameterValues()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IRepro
            {
                void A(int value = 0);
            }
 
            class Repro : IRepro
            {
                public void [||]A(int value = 1)
                {
                }
            }
            """,
            """
            interface IRepro
            {
                void A(int value = 0);
            }
 
            class Repro : IRepro
            {
                void IRepro.A(int value = 1)
                {
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52020")]
    public async Task TestWithMismatchedDefault1()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IRepro
            {
                void A(int value);
            }
 
            class Repro : IRepro
            {
                public void [||]A(int value = 1)
                {
                }
            }
            """,
            """
            interface IRepro
            {
                void A(int value);
            }
 
            class Repro : IRepro
            {
                void IRepro.A(int value = 1)
                {
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52020")]
    public async Task TestWithMismatchedDefault2()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IRepro
            {
                void A(int value = 0);
            }
 
            class Repro : IRepro
            {
                public void [||]A(int value)
                {
                }
            }
            """,
            """
            interface IRepro
            {
                void A(int value = 0);
            }
 
            class Repro : IRepro
            {
                void IRepro.A(int value)
                {
                }
            }
            """);
    }
 
    [Fact]
    public async Task TestPreserveReadOnly()
    {
        await TestInRegularAndScriptAsync(
            """
            interface IRepro
            {
                void A();
            }
 
            class Repro : IRepro
            {
                public readonly void [||]A()
                {
                }
            }
            """,
            """
            interface IRepro
            {
                void A();
            }
 
            class Repro : IRepro
            {
                readonly void IRepro.A()
                {
                }
            }
            """);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72024")]
    public async Task TestFieldEvent()
    {
        await TestInRegularAndScriptAsync(
            """
            using System;
 
            interface IGoo { event Action E; void M(); }
 
            class C : IGoo
            {
                public event Action E;
                public void [||]M() { }
            }
            """,
            """
            using System;
            
            interface IGoo { event Action E; void M(); }
            
            class C : IGoo
            {
                public event Action E;
                void IGoo.M() { }
            }
            """, index: SameInterface);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72024")]
    public async Task TestPropertyEvent()
    {
        await TestInRegularAndScriptAsync(
            """
            using System;
 
            interface IGoo { event Action E; }
 
            class C : IGoo
            {
                public event Action [||]E { add { } remove { } };
            }
            """,
            """
            using System;
            
            interface IGoo { event Action E; }
            
            class C : IGoo
            {
                event Action IGoo.E { add { } remove { } };
            }
            """, index: SingleMember);
    }
}