File: CodeGeneration\CodeGenerationTests.CSharp.cs
Web Access
Project: src\src\EditorFeatures\Test\Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.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.
 
#nullable disable
 
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using CS = Microsoft.CodeAnalysis.CSharp;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.CodeGeneration;
 
using static CSharpSyntaxTokens;
 
[Trait(Traits.Feature, Traits.Features.CodeGeneration)]
public sealed partial class CodeGenerationTests
{
    [UseExportProvider]
    public class CSharp
    {
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddNamespace()
            => TestAddNamespaceAsync("namespace [|N1|] { }", """
                namespace N1 {
                    namespace N2
                    {
                    }
                }
                """,
                name: "N2");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddField()
            => TestAddFieldAsync("class [|C|] { }", """
                class C
                {
                    public int F;
                }
                """,
                type: GetTypeSymbol(typeof(int)));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddStaticField()
            => TestAddFieldAsync("class [|C|] { }", """
                class C
                {
                    private static string F;
                }
                """,
                type: GetTypeSymbol(typeof(string)),
                accessibility: Accessibility.Private,
                modifiers: new Editing.DeclarationModifiers(isStatic: true));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddArrayField()
            => TestAddFieldAsync("class [|C|] { }", """
                class C
                {
                    public int[] F;
                }
                """,
                type: CreateArrayType(typeof(int)));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddUnsafeField()
            => TestAddFieldAsync("class [|C|] { }", """
                class C
                {
                    public unsafe int F;
                }
                """,
                modifiers: new Editing.DeclarationModifiers(isUnsafe: true),
                type: GetTypeSymbol(typeof(int)));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddFieldToCompilationUnit()
            => TestAddFieldAsync("", "public int F;\n",
                type: GetTypeSymbol(typeof(int)), addToCompilationUnit: true);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddConstructor()
            => TestAddConstructorAsync("class [|C|] { }", """
                class C
                {
                    public C()
                    {
                    }
                }
                """);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddConstructorWithoutBody()
            => TestAddConstructorAsync("class [|C|] { }", """
                class C
                {
                    public C();
                }
                """,
                context: new CodeGenerationContext(generateMethodBodies: false));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddConstructorResolveNamespaceImport()
            => TestAddConstructorAsync("class [|C|] { }", """
                using System;
 
                class C
                {
                    public C(DateTime dt, int i)
                    {
                    }
                }
                """,
                parameters: Parameters(Parameter(typeof(DateTime), "dt"), Parameter(typeof(int), "i")));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddChainedConstructor()
            => TestAddConstructorAsync("class [|C|] { public C(int i) { } }", "class C { public C() : this(42) { } public C(int i) { } }",
                thisArguments: [CS.SyntaxFactory.ParseExpression("42")]);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddStaticConstructor()
            => TestAddConstructorAsync("class [|C|] { }", """
                class C
                {
                    static C()
                    {
                    }
                }
                """,
                modifiers: new Editing.DeclarationModifiers(isStatic: true));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544082")]
        public Task AddClass()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    public class C
                    {
                    }
                }
                """);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddClassEscapeName()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    public class @class
                    {
                    }
                }
                """,
                name: "class");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddClassUnicodeName()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    public class classæøå
                    {
                    }
                }
                """,
                name: "classæøå");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")]
        public Task AddStaticClass()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    public static class C
                    {
                    }
                }
                """,
                modifiers: new Editing.DeclarationModifiers(isStatic: true));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")]
        public Task AddStaticAbstractClass()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    public static class C
                    {
                    }
                }
                """,
                modifiers: new Editing.DeclarationModifiers(isStatic: true, isAbstract: true));
 
        [Theory, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")]
        [InlineData(Accessibility.NotApplicable)]
        [InlineData(Accessibility.Internal)]
        [InlineData(Accessibility.Public)]
        public Task AddFileClass(Accessibility accessibility)
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    file class C
                    {
                    }
                }
                """,
                accessibility: accessibility,
                modifiers: new Editing.DeclarationModifiers(isFile: true));
 
        [Theory, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")]
        [InlineData("struct", TypeKind.Struct)]
        [InlineData("interface", TypeKind.Interface)]
        [InlineData("enum", TypeKind.Enum)]
        public async Task AddFileType(string kindString, TypeKind typeKind)
        {
            var expected = """
                namespace N
                {
                    file 
                """ + kindString + """
                 C
                    {
                    }
                }
                """;
            await TestAddNamedTypeAsync("namespace [|N|] { }", expected,
                typeKind: typeKind,
                modifiers: new Editing.DeclarationModifiers(isFile: true));
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")]
        public Task AddSealedClass()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    private sealed class C
                    {
                    }
                }
                """,
                accessibility: Accessibility.Private,
                modifiers: new Editing.DeclarationModifiers(isSealed: true));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")]
        public Task AddAbstractClass()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    protected internal abstract class C
                    {
                    }
                }
                """,
                accessibility: Accessibility.ProtectedOrInternal,
                modifiers: new Editing.DeclarationModifiers(isAbstract: true));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddStruct()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    internal struct S
                    {
                    }
                }
                """,
                name: "S",
                accessibility: Accessibility.Internal,
                typeKind: TypeKind.Struct);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546224")]
        public Task AddSealedStruct()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    public struct S
                    {
                    }
                }
                """,
                name: "S",
                modifiers: new Editing.DeclarationModifiers(isSealed: true),
                accessibility: Accessibility.Public,
                typeKind: TypeKind.Struct);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddInterface()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    public interface I
                    {
                    }
                }
                """,
                name: "I",
                typeKind: TypeKind.Interface);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544080")]
        public Task AddEnum()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    public enum E
                    {
                    }
                }
                """, "E",
                typeKind: TypeKind.Enum);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544527")]
        public Task AddEnumWithValues()
            => TestAddNamedTypeAsync("namespace [|N|] { }", """
                namespace N
                {
                    public enum E
                    {
                        F1 = 1,
                        F2 = 2
                    }
                }
                """, "E",
                typeKind: TypeKind.Enum,
                members: Members(CreateEnumField("F1", 1), CreateEnumField("F2", 2)));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544080")]
        public Task AddDelegateType()
            => TestAddDelegateTypeAsync("class [|C|] { }", """
                class C
                {
                    public delegate int D(string s);
                }
                """,
                returnType: typeof(int),
                parameters: Parameters(Parameter(typeof(string), "s")));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546224")]
        public Task AddSealedDelegateType()
            => TestAddDelegateTypeAsync("class [|C|] { }", """
                class C
                {
                    public delegate int D(string s);
                }
                """,
                returnType: typeof(int),
                parameters: Parameters(Parameter(typeof(string), "s")),
                modifiers: new Editing.DeclarationModifiers(isSealed: true));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddEvent()
            => TestAddEventAsync("class [|C|] { }", """
                class C
                {
                    public event System.Action E;
                }
                """,
                context: new CodeGenerationContext(addImports: false));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public async Task AddCustomEventToClassFromSourceSymbol()
        {
            var context = new CodeGenerationContext(reuseSyntax: true);
            await TestGenerateFromSourceSymbolAsync("""
                class [|C2|]
                {
                    event EventHandler Click
                    {
                        add
                        {
                            Events.AddHandler("ClickEvent", value)
                        }
                        remove
                        {
                            Events.RemoveHandler("ClickEvent", value)
                        }
                    }
                }
                """, "class [|C1|] { }", """
                class C1
                {
                    event EventHandler Click
                    {
                        add
                        {
                            Events.AddHandler("ClickEvent", value)
                        }
                        remove
                        {
                            Events.RemoveHandler("ClickEvent", value)
                        }
                    }
                }
                """, onlyGenerateMembers: true, context: context);
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddUnsafeEvent()
            => TestAddEventAsync("class [|C|] { }", """
                class C
                {
                    public unsafe event System.Action E;
                }
                """,
                modifiers: new Editing.DeclarationModifiers(isUnsafe: true),
                context: new CodeGenerationContext(addImports: false));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddEventWithAccessors()
            => TestAddEventAsync("class [|C|] { }", """
                class C
                {
                    public event System.Action E
                    {
                        add
                        {
                        }
 
                        remove
                        {
                        }
                    }
                }
                """,
                addMethod: CodeGenerationSymbolFactory.CreateAccessorSymbol([], Accessibility.NotApplicable, []),
                removeMethod: CodeGenerationSymbolFactory.CreateAccessorSymbol([], Accessibility.NotApplicable, []),
                context: new CodeGenerationContext(addImports: false));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddMethodToClass()
            => TestAddMethodAsync("class [|C|] { }", """
                class C
                {
                    public void M()
                    {
                    }
                }
                """,
                returnType: typeof(void));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public async Task AddMethodToClassFromSourceSymbol()
        {
            var context = new CodeGenerationContext(reuseSyntax: true);
            await TestGenerateFromSourceSymbolAsync("""
                class [|C2|]
                {
                    public int FInt()
                    {
                        return 0;
                    }
                }
                """, "class [|C1|] { }", """
                class C1
                {
                    public int FInt()
                    {
                        return 0;
                    }
                }
                """, onlyGenerateMembers: true, context: context);
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddMethodToClassEscapedName()
            => TestAddMethodAsync("class [|C|] { }", """
                using System;
 
                class C
                {
                    public DateTime @static()
                    {
                    }
                }
                """,
                name: "static",
                returnType: typeof(DateTime));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddStaticMethodToStruct()
            => TestAddMethodAsync("struct [|S|] { }", """
                struct S
                {
                    public static int M()
                    {
                        $$
                    }
                }
                """,
                modifiers: new Editing.DeclarationModifiers(isStatic: true),
                returnType: typeof(int),
                statements: "return 0;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddSealedOverrideMethod()
            => TestAddMethodAsync("class [|C|] { }", """
                class C
                {
                    public sealed override int GetHashCode()
                    {
                        $$
                    }
                }
                """,
                name: "GetHashCode",
                modifiers: new Editing.DeclarationModifiers(isOverride: true, isSealed: true),
                returnType: typeof(int),
                statements: "return 0;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAbstractMethod()
            => TestAddMethodAsync("abstract class [|C|] { }", """
                abstract class C
                {
                    public abstract int M();
                }
                """,
                modifiers: new Editing.DeclarationModifiers(isAbstract: true),
                returnType: typeof(int));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddMethodWithoutBody()
            => TestAddMethodAsync("class [|C|] { }", """
                class C
                {
                    public int M();
                }
                """,
                returnType: typeof(int),
                context: new CodeGenerationContext(generateMethodBodies: false));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddGenericMethod()
            => TestAddMethodAsync("class [|C|] { }", """
                class C
                {
                    public int M<T>()
                    {
                        $$
                    }
                }
                """,
                returnType: typeof(int),
                typeParameters: [CodeGenerationSymbolFactory.CreateTypeParameterSymbol("T")],
                statements: "return new T().GetHashCode();");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddVirtualMethod()
            => TestAddMethodAsync("class [|C|] { }", """
                class C
                {
                    protected virtual int M()
                    {
                        $$
                    }
                }
                """,
                accessibility: Accessibility.Protected,
                modifiers: new Editing.DeclarationModifiers(isVirtual: true),
                returnType: typeof(int),
                statements: "return 0;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddUnsafeNewMethod()
            => TestAddMethodAsync("class [|C|] { }", """
                class C
                {
                    public unsafe new string ToString()
                    {
                        $$
                    }
                }
                """,
                name: "ToString",
                modifiers: new Editing.DeclarationModifiers(isNew: true, isUnsafe: true),
                returnType: typeof(string),
                statements: "return String.Empty;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public async Task AddExplicitImplementationOfUnsafeMethod()
        {
            var input = "interface I { unsafe void M(int i); } class [|C|] : I { }";
            await TestAddMethodAsync(input, """
                interface I { unsafe void M(int i); }
                class C : I
                {
                    unsafe void I.M(int i)
                    {
                    }
                }
                """,
                name: "M",
                returnType: typeof(void),
                parameters: Parameters(Parameter(typeof(int), "i")),
                modifiers: new Editing.DeclarationModifiers(isUnsafe: true),
                getExplicitInterfaces: s => [.. s.LookupSymbols(input.IndexOf('M'), null, "M").OfType<IMethodSymbol>()]);
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public async Task AddExplicitImplementation()
        {
            var input = "interface I { void M(int i); } class [|C|] : I { }";
            await TestAddMethodAsync(input, """
                interface I { void M(int i); }
                class C : I
                {
                    void I.M(int i)
                    {
                    }
                }
                """,
                name: "M",
                returnType: typeof(void),
                parameters: Parameters(Parameter(typeof(int), "i")),
                getExplicitInterfaces: s => [.. s.LookupSymbols(input.IndexOf('M'), null, "M").OfType<IMethodSymbol>()]);
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddTrueFalseOperators()
            => TestAddOperatorsAsync("""
                class [|C|]
                {
                }
                """, """
                class C
                {
                    public static bool operator true(C other)
                    {
                        $$
                    }
 
                    public static bool operator false(C other)
                    {
                        $$
                    }
                }
                """,
                [CodeGenerationOperatorKind.True, CodeGenerationOperatorKind.False],
                parameters: Parameters(Parameter("C", "other")),
                returnType: typeof(bool),
                statements: "return false;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddUnaryOperators()
            => TestAddOperatorsAsync("""
                class [|C|]
                {
                }
                """, """
                class C
                {
                    public static object operator +(C other)
                    {
                        $$
                    }
 
                    public static object operator -(C other)
                    {
                        $$
                    }
 
                    public static object operator !(C other)
                    {
                        $$
                    }
 
                    public static object operator ~(C other)
                    {
                        $$
                    }
 
                    public static object operator ++(C other)
                    {
                        $$
                    }
 
                    public static object operator --(C other)
                    {
                        $$
                    }
                }
                """,
                [
                    CodeGenerationOperatorKind.UnaryPlus,
                    CodeGenerationOperatorKind.UnaryNegation,
                    CodeGenerationOperatorKind.LogicalNot,
                    CodeGenerationOperatorKind.OnesComplement,
                    CodeGenerationOperatorKind.Increment,
                    CodeGenerationOperatorKind.Decrement
                ],
                parameters: Parameters(Parameter("C", "other")),
                returnType: typeof(object),
                statements: "return null;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddBinaryOperators()
            => TestAddOperatorsAsync("""
                class [|C|]
                {
                }
                """, """
                class C
                {
                    public static object operator +(C a, C b)
                    {
                        $$
                    }
 
                    public static object operator -(C a, C b)
                    {
                        $$
                    }
 
                    public static object operator *(C a, C b)
                    {
                        $$
                    }
 
                    public static object operator /(C a, C b)
                    {
                        $$
                    }
 
                    public static object operator %(C a, C b)
                    {
                        $$
                    }
 
                    public static object operator &(C a, C b)
                    {
                        $$
                    }
 
                    public static object operator |(C a, C b)
                    {
                        $$
                    }
 
                    public static object operator ^(C a, C b)
                    {
                        $$
                    }
 
                    public static object operator <<(C a, C b)
                    {
                        $$
                    }
 
                    public static object operator >>(C a, C b)
                    {
                        $$
                    }
                }
                """,
                [
                    CodeGenerationOperatorKind.Addition,
                    CodeGenerationOperatorKind.Subtraction,
                    CodeGenerationOperatorKind.Multiplication,
                    CodeGenerationOperatorKind.Division,
                    CodeGenerationOperatorKind.Modulus,
                    CodeGenerationOperatorKind.BitwiseAnd,
                    CodeGenerationOperatorKind.BitwiseOr,
                    CodeGenerationOperatorKind.ExclusiveOr,
                    CodeGenerationOperatorKind.LeftShift,
                    CodeGenerationOperatorKind.RightShift
                ],
                parameters: Parameters(Parameter("C", "a"), Parameter("C", "b")),
                returnType: typeof(object),
                statements: "return null;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddComparisonOperators()
            => TestAddOperatorsAsync("""
                class [|C|]
                {
                }
                """, """
                class C
                {
                    public static bool operator ==(C a, C b)
                    {
                        $$
                    }
 
                    public static bool operator !=(C a, C b)
                    {
                        $$
                    }
 
                    public static bool operator <(C a, C b)
                    {
                        $$
                    }
 
                    public static bool operator >(C a, C b)
                    {
                        $$
                    }
 
                    public static bool operator <=(C a, C b)
                    {
                        $$
                    }
 
                    public static bool operator >=(C a, C b)
                    {
                        $$
                    }
                }
                """,
                [
                    CodeGenerationOperatorKind.Equality,
                    CodeGenerationOperatorKind.Inequality,
                    CodeGenerationOperatorKind.GreaterThan,
                    CodeGenerationOperatorKind.LessThan,
                    CodeGenerationOperatorKind.LessThanOrEqual,
                    CodeGenerationOperatorKind.GreaterThanOrEqual
                ],
                parameters: Parameters(Parameter("C", "a"), Parameter("C", "b")),
                returnType: typeof(bool),
                statements: "return true;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddUnsupportedOperator()
            => TestAddUnsupportedOperatorAsync("class [|C|] { }",
                operatorKind: CodeGenerationOperatorKind.Like,
                parameters: Parameters(Parameter("C", "a"), Parameter("C", "b")),
                returnType: typeof(bool),
                statements: "return true;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddExplicitConversion()
            => TestAddConversionAsync(@"class [|C|] { }", """
                class C
                {
                    public static explicit operator int(C other)
                    {
                        $$
                    }
                }
                """,
                toType: typeof(int),
                fromType: Parameter("C", "other"),
                statements: "return 0;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddImplicitConversion()
            => TestAddConversionAsync(@"class [|C|] { }", """
                class C
                {
                    public static implicit operator int(C other)
                    {
                        $$
                    }
                }
                """,
                toType: typeof(int),
                fromType: Parameter("C", "other"),
                isImplicit: true,
                statements: "return 0;");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddStatements()
            => TestAddStatementsAsync("class C { public void [|M|]() { Console.WriteLine(1); } }", "class C { public void M() { Console.WriteLine(1); $$} }", "Console.WriteLine(2);");
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/840265")]
        public Task AddDefaultParameterWithNonDefaultValueToMethod()
            => TestAddParametersAsync("class C { public void [|M|]() { } }", "class C { public void M(string text =\"Hello\") { } }",
                Parameters(Parameter(typeof(string), "text", true, "Hello")));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddDefaultParameterWithDefaultValueToMethod()
            => TestAddParametersAsync("class C { public void [|M|]() { } }", "class C { public void M(double number =0) { } }",
                Parameters(Parameter(typeof(double), "number", true)));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddParametersToMethod()
            => TestAddParametersAsync("class C { public void [|M|]() { } }", "class C { public void M(int num, string text =\"Hello!\", float floating =0.5F) { } }",
                Parameters(Parameter(typeof(int), "num"), Parameter(typeof(string), "text", true, "Hello!"), Parameter(typeof(float), "floating", true, .5f)));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/841365")]
        public Task AddParamsParameterToMethod()
            => TestAddParametersAsync("class C { public void [|M|]() { } }", "class C { public void M(params char[]characters) { } }",
                Parameters(Parameter(typeof(char[]), "characters", isParams: true)));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544015")]
        public Task AddAutoProperty()
            => TestAddPropertyAsync("class [|C|] { }", """
                class C
                {
                    public int P { get; internal set; }
                }
                """,
                type: typeof(int),
                setterAccessibility: Accessibility.Internal);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddUnsafeAutoProperty()
            => TestAddPropertyAsync("class [|C|] { }", """
                class C
                {
                    public unsafe int P { get; internal set; }
                }
                """,
                type: typeof(int),
                modifiers: new Editing.DeclarationModifiers(isUnsafe: true),
                setterAccessibility: Accessibility.Internal);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public async Task AddPropertyToClassFromSourceSymbol()
        {
            var context = new CodeGenerationContext(reuseSyntax: true);
            await TestGenerateFromSourceSymbolAsync("""
                class [|C2|]
                {
                    public int P
                    {
                        get
                        {
                            return 0;
                        }
                    }
                }
                """, "class [|C1|] { }", """
                class C1
                {
                    public int P
                    {
                        get
                        {
                            return 0;
                        }
                    }
                }
                """, onlyGenerateMembers: true, context: context);
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddIndexer1()
            => TestAddPropertyAsync("class [|C|] { }", "class C { public string this[int i] => String.Empty; }",
                type: typeof(string),
                parameters: Parameters(Parameter(typeof(int), "i")),
                getStatements: "return String.Empty;",
                isIndexer: true);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddIndexer2()
            => TestAddPropertyAsync("class [|C|] { }", """
                class C
                {
                    public string this[int i]
                    {
                        get
                        {
                            $$
                        }
                    }
                }
                """,
                type: typeof(string),
                parameters: Parameters(Parameter(typeof(int), "i")),
                getStatements: "return String.Empty;",
                isIndexer: true,
                options: new OptionsCollection(LanguageNames.CSharp)
                {
                    { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement },
                    { CSharpCodeStyleOptions.PreferExpressionBodiedIndexers, CSharpCodeStyleOptions.NeverWithSilentEnforcement },
                });
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddParameterfulProperty()
            => TestAddPropertyAsync("class [|C|] { }", """
                class C
                {
                    public string get_P(int i, int j)
                    {
                        $$
                    }
 
                    public void set_P(int i, int j, string value)
                    {
                    }
                }
                """,
                type: typeof(string),
                getStatements: "return String.Empty;",
                setStatements: "",
                parameters: Parameters(Parameter(typeof(int), "i"), Parameter(typeof(int), "j")));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToTypes()
            => TestAddAttributeAsync("class [|C|] { }", """
                [System.Serializable]
                class C { }
                """, typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromTypes()
            => TestRemoveAttributeAsync<SyntaxNode>(@"[System.Serializable] class [|C|] { }", "class C { }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToMethods()
            => TestAddAttributeAsync("class C { public void [|M()|] { } }", "class C { [System.Serializable] public void M() { } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromMethods()
            => TestRemoveAttributeAsync<SyntaxNode>("class C { [System.Serializable] public void [|M()|] { } }", "class C { public void M() { } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToFields()
            => TestAddAttributeAsync("class C { [|public int F|]; }", "class C { [System.Serializable] public int F; }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromFields()
            => TestRemoveAttributeAsync<FieldDeclarationSyntax>("class C { [System.Serializable] public int [|F|]; }", "class C { public int F; }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToProperties()
            => TestAddAttributeAsync("class C { public int [|P|] { get; set; }}", "class C { [System.Serializable] public int P { get; set; } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromProperties()
            => TestRemoveAttributeAsync<SyntaxNode>("class C { [System.Serializable] public int [|P|] { get; set; }}", "class C { public int P { get; set; } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToPropertyAccessor()
            => TestAddAttributeAsync("class C { public int P { [|get|]; set; }}", "class C { public int P { [System.Serializable] get; set; }}", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromPropertyAccessor()
            => TestRemoveAttributeAsync<SyntaxNode>("class C { public int P { [System.Serializable] [|get|]; set; } }", "class C { public int P { get; set; } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToEnums()
            => TestAddAttributeAsync("enum [|C|] { One, Two }", """
                [System.Serializable]
                enum C { One, Two }
                """, typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromEnums()
            => TestRemoveAttributeAsync<SyntaxNode>("[System.Serializable] enum [|C|] { One, Two }", "enum C { One, Two }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToEnumMembers()
            => TestAddAttributeAsync("enum C { [|One|], Two }", "enum C { [System.Serializable] One, Two }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromEnumMembers()
            => TestRemoveAttributeAsync<SyntaxNode>("enum C { [System.Serializable] [|One|], Two }", "enum C { One, Two }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToIndexer()
            => TestAddAttributeAsync("class C { public int [|this[int y]|] { get; set; }}", "class C { [System.Serializable] public int this[int y] { get; set; } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromIndexer()
            => TestRemoveAttributeAsync<SyntaxNode>("class C { [System.Serializable] public int [|this[int y]|] { get; set; }}", "class C { public int this[int y] { get; set; } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToOperator()
            => TestAddAttributeAsync("class C { public static C operator [|+|] (C c1, C c2) { return new C(); }}", "class C { [System.Serializable] public static C operator +(C c1, C c2) { return new C(); } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromOperator()
            => TestRemoveAttributeAsync<SyntaxNode>("class C { [System.Serializable] public static C operator [|+|](C c1, C c2) { return new C(); }}", "class C { public static C operator +(C c1, C c2) { return new C(); } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToDelegate()
            => TestAddAttributeAsync("delegate int [|D()|];", """
                [System.Serializable]
                delegate int D();
                """, typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromDelegate()
            => TestRemoveAttributeAsync<SyntaxNode>("[System.Serializable] delegate int [|D()|];", "delegate int D();", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToParam()
            => TestAddAttributeAsync("class C { public void M([|int x|]) { } }", "class C { public void M([System.Serializable] int x) { } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromParam()
            => TestRemoveAttributeAsync<SyntaxNode>("class C { public void M([System.Serializable] [|int x|]) { } }", "class C { public void M(int x) { } }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToTypeParam()
            => TestAddAttributeAsync("class C<[|T|]> { }", "class C<[System.Serializable] T> { }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeFromTypeParam()
            => TestRemoveAttributeAsync<SyntaxNode>("class C<[System.Serializable] [|T|]> { }", "class C<T> { }", typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeToCompilationUnit()
            => TestAddAttributeAsync("[|class C { } class D {} |]", """
                [assembly: System.Serializable]
 
                class C { }
                class D { }
                """, typeof(SerializableAttribute), AssemblyKeyword);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeWithWrongTarget()
            => Assert.ThrowsAsync<AggregateException>(async () =>
                await TestAddAttributeAsync("[|class C { } class D {} |]", "", typeof(SerializableAttribute), RefKeyword));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task AddAttributeWithArrayParams()
            => TestAddAttributeAsync("""
                using System; 
                class ExampleAttribute : Attribute
                {
                    public ExampleAttribute(int[] items) { }
                }
                class ExampleType
                {
                    [Example(new[] { 1, 2, 3 })]
                    public void {|method:M|}() { }
                }
                class C
                {
                    public void [|M|]2() { }
                }
                """, """
                using System; 
                class ExampleAttribute : Attribute
                {
                    public ExampleAttribute(int[] items) { }
                }
                class ExampleType
                {
                    [Example(new[] { 1, 2, 3 })]
                    public void M() { }
                }
                class C
                {
                    [Example(new[] { 1, 2, 3 })]
                    public void M2() { }
                }
                """, (context) =>
            {
                var method = context.GetAnnotatedDeclaredSymbols("method", context.SemanticModel).Single();
                var attribute = method.GetAttributes().Single();
                return attribute;
            });
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeWithTrivia()
            => TestRemoveAttributeAsync<SyntaxNode>("""
                // Comment 1
                [System.Serializable] // Comment 2
                /* Comment 3*/ class [|C|] { }
                """, """
                // Comment 1
                /* Comment 3*/ class C { }
                """, typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeWithTrivia_NewLine()
            => TestRemoveAttributeAsync<SyntaxNode>("""
                // Comment 1
                [System.Serializable]
                /* Comment 3*/ class [|C|] { }
                """, """
                // Comment 1
                /* Comment 3*/ class C { }
                """, typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeWithMultipleAttributes()
            => TestRemoveAttributeAsync<SyntaxNode>("""
                // Comment 1
                /*Comment2*/[ /*Comment3*/ System.Serializable /*Comment4*/, /*Comment5*/System.Flags /*Comment6*/] /*Comment7*/
                /* Comment 8*/
                class [|C|] { }
                """, """
                // Comment 1
                /*Comment2*/[ /*Comment3*/  /*Comment5*/System.Flags /*Comment6*/] /*Comment7*/
                /* Comment 8*/
                class C { }
                """, typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task RemoveAttributeWithMultipleAttributeLists()
            => TestRemoveAttributeAsync<SyntaxNode>("""
                // Comment 1
                /*Comment2*/[ /*Comment3*/ System.Serializable /*Comment4*/, /*Comment5*/System.Flags /*Comment6*/] /*Comment7*/
                [ /*Comment9*/ System.Obsolete /*Comment10*/] /*Comment11*/
                /* Comment12*/
                class [|C|] { }
                """, """
                // Comment 1
                /*Comment2*/[ /*Comment3*/  /*Comment5*/System.Flags /*Comment6*/] /*Comment7*/
                [ /*Comment9*/ System.Obsolete /*Comment10*/] /*Comment11*/
                /* Comment12*/
                class C { }
                """, typeof(SerializableAttribute));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public async Task TestUpdateModifiers()
        {
            var eol = SyntaxFactory.EndOfLine(@"");
            var newModifiers = new[] { InternalKeyword.WithLeadingTrivia(eol) }.Concat(
                CreateModifierTokens(new Editing.DeclarationModifiers(isSealed: true, isPartial: true), LanguageNames.CSharp));
 
            await TestUpdateDeclarationAsync<ClassDeclarationSyntax>("""
                public static class [|C|] // Comment 1
                {
                    // Comment 2
                }
                """, """
                internal partial sealed class C // Comment 1
                {
                    // Comment 2
                }
                """, modifiers: newModifiers);
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task TestUpdateAccessibility()
            => TestUpdateDeclarationAsync<ClassDeclarationSyntax>("""
                // Comment 0
                public static class [|C|] // Comment 1
                {
                    // Comment 2
                }
                """, """
                // Comment 0
                internal static class C // Comment 1
                {
                    // Comment 2
                }
                """, accessibility: Accessibility.Internal);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public Task TestUpdateDeclarationType()
            => TestUpdateDeclarationAsync<MethodDeclarationSyntax>("""
                public static class C
                {
                    // Comment 1
                    public static char [|F|]() { return 0; }
                }
                """, """
                public static class C
                {
                    // Comment 1
                    public static int F() { return 0; }
                }
                """, getType: GetTypeSymbol(typeof(int)));
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public async Task TestUpdateDeclarationMembers()
        {
            var getField = CreateField(Accessibility.Public, new Editing.DeclarationModifiers(), typeof(int), "f2");
            var getMembers = ImmutableArray.Create(getField);
            await TestUpdateDeclarationAsync<ClassDeclarationSyntax>(
                """
                public static class [|C|]
                {
                    // Comment 0
                    public int {|RetainedMember:f|};
 
                    // Comment 1
                    public static char F() { return 0; }
                }
                """, """
                public static class C
                {
                    // Comment 0
                    public int f;
                    public int f2;
                }
                """, getNewMembers: getMembers);
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
        public async Task TestUpdateDeclarationMembers_DifferentOrder()
        {
            var getField = CreateField(Accessibility.Public, new Editing.DeclarationModifiers(), typeof(int), "f2");
            var getMembers = ImmutableArray.Create(getField);
            await TestUpdateDeclarationAsync<ClassDeclarationSyntax>("""
                public static class [|C|]
                {
                    // Comment 0
                    public int f;
 
                    // Comment 1
                    public static char {|RetainedMember:F|}() { return 0; }
                }
                """, """
                public static class C
                {
                    public int f2;
 
                    // Comment 1
                    public static char F() { return 0; }
                }
                """, getNewMembers: getMembers, declareNewMembersAtTop: true);
        }
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGenerationSortDeclarations)]
        public Task SortAroundDestructor()
            => TestGenerateFromSourceSymbolAsync("public class [|C|] { public C(){} public int this[int index]{get{return 0;}set{value = 0;}} }", "public class [|C|] { ~C(){} }", """
                public class C {
                    public C()
                    {
                    }
 
                    ~C(){}
 
                    public int this[int index]
                    {
                        get
                        {
                        }
 
                        set
                        {
                        }
                    }
                }
                """, onlyGenerateMembers: true);
 
        [Fact, Trait(Traits.Feature, Traits.Features.CodeGenerationSortDeclarations)]
        public Task SortOperators()
            => TestGenerateFromSourceSymbolAsync("""
                namespace N
                {
                    public class [|C|]
                    {
                        // Unary operators
                        public static bool operator false (C other) { return false; }
                        public static bool operator true (C other) { return true; }
                        public static C operator ++ (C other) { return null; }
                        public static C operator -- (C other) { return null; }
                        public static C operator ~ (C other) { return null; }
                        public static C operator ! (C other) { return null; }
                        public static C operator - (C other) { return null; }
                        public static C operator + (C other) { return null; }
 
                        // Binary operators
                        public static C operator >> (C a, int shift) { return null; }
                        public static C operator << (C a, int shift) { return null; }
                        public static C operator ^ (C a, C b) { return null; }
                        public static C operator | (C a, C b) { return null; }
                        public static C operator & (C a, C b) { return null; }
                        public static C operator % (C a, C b) { return null; }
                        public static C operator / (C a, C b) { return null; }
                        public static C operator * (C a, C b) { return null; }
                        public static C operator - (C a, C b) { return null; }
                        public static C operator + (C a, C b) { return null; }
 
                        // Comparison operators
                        public static bool operator >= (C a, C b) { return true; }
                        public static bool operator <= (C a, C b) { return true; }
                        public static bool operator > (C a, C b) { return true; }
                        public static bool operator < (C a, C b) { return true; }
                        public static bool operator != (C a, C b) { return true; }
                        public static bool operator == (C a, C b) { return true; }
                    }
                }
                """, "namespace [|N|] { }", """
                namespace N
                {
                    public class C
                    {
                        public static C operator +(C other);
                        public static C operator +(C a, C b);
                        public static C operator -(C other);
                        public static C operator -(C a, C b);
                        public static C operator !(C other);
                        public static C operator ~(C other);
                        public static C operator ++(C other);
                        public static C operator --(C other);
                        public static C operator *(C a, C b);
                        public static C operator /(C a, C b);
                        public static C operator %(C a, C b);
                        public static C operator &(C a, C b);
                        public static C operator |(C a, C b);
                        public static C operator ^(C a, C b);
                        public static C operator <<(C a, int shift);
                        public static C operator >>(C a, int shift);
                        public static bool operator ==(C a, C b);
                        public static bool operator !=(C a, C b);
                        public static bool operator <(C a, C b);
                        public static bool operator >(C a, C b);
                        public static bool operator <=(C a, C b);
                        public static bool operator >=(C a, C b);
                        public static bool operator true(C other);
                        public static bool operator false(C other);
                    }
                }
                """,
                forceLanguage: LanguageNames.CSharp,
                context: new CodeGenerationContext(generateMethodBodies: false));
    }
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/665008")]
    public Task TestExtensionMethods()
        => TestGenerateFromSourceSymbolAsync("""
            public static class [|C|]
            {
                public static void ExtMethod1(this string s, int y, string z) {}
            }
            """, "public static class [|C|] {}", """
            public static class C
            {
                public static void ExtMethod1(this string s, int y, string z);
            }
            """,
            context: new CodeGenerationContext(generateMethodBodies: false),
            onlyGenerateMembers: true);
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530829")]
    public Task TestVBPropertiesWithParams()
        => TestGenerateFromSourceSymbolAsync("""
            Namespace N
                Public Class [|C|]
                    Public Overridable Property IndexProp(ByVal p1 As Integer) As String
                        Get
                            Return Nothing
                        End Get
                        Set(ByVal value As String)
                        End Set
                    End Property
                End Class
            End Namespace
            """, "namespace [|N|] {}", """
            namespace N
            {
                public class C
                {
                    public virtual string get_IndexProp(int p1);
                    public virtual void set_IndexProp(int p1, string value);
                }
            }
            """,
            context: new CodeGenerationContext(generateMethodBodies: false));
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/812738")]
    public Task TestRefParamsWithDefaultValue()
        => TestGenerateFromSourceSymbolAsync("""
            Public Class [|C|]
                Public Sub Goo(x As Integer, Optional ByRef y As Integer = 10, Optional ByRef z As Object = Nothing)
                End Sub
            End Class
            """, "public class [|C|] {}", """
            public class C
            {
                public void Goo(int x, ref int y, ref object z);
            }
            """,
            context: new CodeGenerationContext(generateMethodBodies: false),
            onlyGenerateMembers: true);
 
    [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/848357")]
    public Task TestConstraints()
        => TestGenerateFromSourceSymbolAsync("""
            namespace N
            {
                public class [|C|]<T, U> where T : struct where U : class
                {
                    public void Goo<Q, R>() where Q : new() where R : IComparable { }
                    public delegate void D<T, U>(T t, U u) where T : struct where U : class;
                }
            }
            """, "namespace [|N|] {}", """
            namespace N
            {
                public class C<T, U>
                    where T : struct
                    where U : class
                {
                    public void Goo<Q, R>()
                        where Q : new()
                        where R : IComparable;
 
                    public delegate void D<T, U>(T t, U u)
                        where T : struct
                        where U : class;
                }
            }
            """,
            context: new CodeGenerationContext(generateMethodBodies: false),
            onlyGenerateMembers: true);
}