﻿// 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.Generic;
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.Options;
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
{
    [Trait(Traits.Feature, Traits.Features.CodeGeneration)]
    public partial class CodeGenerationTests
    {
        [UseExportProvider]
        public class CSharp
        {
            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddNamespace()
            {
                var input = "namespace [|N1|] { }";
                var expected = @"namespace N1 {
    namespace N2
    {
    }
}";
                await TestAddNamespaceAsync(input, expected,
                    name: "N2");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddField()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public int F;
}";
                await TestAddFieldAsync(input, expected,
                    type: GetTypeSymbol(typeof(int)));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddStaticField()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    private static string F;
}";
                await TestAddFieldAsync(input, expected,
                    type: GetTypeSymbol(typeof(string)),
                    accessibility: Accessibility.Private,
                    modifiers: new Editing.DeclarationModifiers(isStatic: true));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddArrayField()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public int[] F;
}";
                await TestAddFieldAsync(input, expected,
                    type: CreateArrayType(typeof(int)));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddUnsafeField()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public unsafe int F;
}";
                await TestAddFieldAsync(input, expected,
                    modifiers: new Editing.DeclarationModifiers(isUnsafe: true),
                    type: GetTypeSymbol(typeof(int)));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddFieldToCompilationUnit()
            {
                var input = "";
                var expected = "public int F;\n";
                await TestAddFieldAsync(input, expected,
                    type: GetTypeSymbol(typeof(int)), addToCompilationUnit: true);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddConstructor()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public C()
    {
    }
}";
                await TestAddConstructorAsync(input, expected);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddConstructorWithoutBody()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public C();
}";
                await TestAddConstructorAsync(input, expected,
                    context: new CodeGenerationContext(generateMethodBodies: false));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddConstructorResolveNamespaceImport()
            {
                var input = "class [|C|] { }";
                var expected = @"using System;

class C
{
    public C(DateTime dt, int i)
    {
    }
}";
                await TestAddConstructorAsync(input, expected,
                    parameters: Parameters(Parameter(typeof(DateTime), "dt"), Parameter(typeof(int), "i")));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddChainedConstructor()
            {
                var input = "class [|C|] { public C(int i) { } }";
                var expected = "class C { public C() : this(42) { } public C(int i) { } }";
                await TestAddConstructorAsync(input, expected,
                    thisArguments: ImmutableArray.Create<SyntaxNode>(CS.SyntaxFactory.ParseExpression("42")));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddStaticConstructor()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    static C()
    {
    }
}";
                await TestAddConstructorAsync(input, expected,
                    modifiers: new Editing.DeclarationModifiers(isStatic: true));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544082")]
            public async Task AddClass()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    public class C
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddClassEscapeName()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    public class @class
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected,
                    name: "class");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddClassUnicodeName()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    public class classæøå
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected,
                    name: "classæøå");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")]
            public async Task AddStaticClass()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    public static class C
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected,
                    modifiers: new Editing.DeclarationModifiers(isStatic: true));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544405")]
            public async Task AddStaticAbstractClass()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    public static class C
    {
    }
}";
                // note that 'abstract' is dropped here
                await TestAddNamedTypeAsync(input, expected,
                    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 async Task AddFileClass(Accessibility accessibility)
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    file class C
    {
    }
}";
                // note: when invalid combinations of modifiers+accessibility are present here,
                // we actually drop the accessibility. This is similar to what is done if someone declares a 'static abstract class C { }'.
                await TestAddNamedTypeAsync(input, expected,
                    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 input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    file " + kindString + @" C
    {
    }
}";
                await TestAddNamedTypeAsync(input, 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 async Task AddSealedClass()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    private sealed class C
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected,
                    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 async Task AddAbstractClass()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    protected internal abstract class C
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected,
                    accessibility: Accessibility.ProtectedOrInternal,
                    modifiers: new Editing.DeclarationModifiers(isAbstract: true));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddStruct()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    internal struct S
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected,
                    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 async Task AddSealedStruct()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    public struct S
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected,
                    name: "S",
                    modifiers: new Editing.DeclarationModifiers(isSealed: true),
                    accessibility: Accessibility.Public,
                    typeKind: TypeKind.Struct);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddInterface()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    public interface I
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected,
                    name: "I",
                    typeKind: TypeKind.Interface);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544080")]
            public async Task AddEnum()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    public enum E
    {
    }
}";
                await TestAddNamedTypeAsync(input, expected, "E",
                    typeKind: TypeKind.Enum);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544527")]
            public async Task AddEnumWithValues()
            {
                var input = "namespace [|N|] { }";
                var expected = @"namespace N
{
    public enum E
    {
        F1 = 1,
        F2 = 2
    }
}";
                await TestAddNamedTypeAsync(input, expected, "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 async Task AddDelegateType()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public delegate int D(string s);
}";
                await TestAddDelegateTypeAsync(input, expected,
                    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 async Task AddSealedDelegateType()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public delegate int D(string s);
}";
                await TestAddDelegateTypeAsync(input, expected,
                    returnType: typeof(int),
                    parameters: Parameters(Parameter(typeof(string), "s")),
                    modifiers: new Editing.DeclarationModifiers(isSealed: true));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddEvent()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public event System.Action E;
}";
                await TestAddEventAsync(input, expected,
                    context: new CodeGenerationContext(addImports: false));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddCustomEventToClassFromSourceSymbol()
            {
                var sourceGenerated = @"class [|C2|]
{
    event EventHandler Click
    {
        add
        {
            Events.AddHandler(""ClickEvent"", value)
        }
        remove
        {
            Events.RemoveHandler(""ClickEvent"", value)
        }
    }
}";
                var input = "class [|C1|] { }";
                var expected = @"class C1
{
    event EventHandler Click
    {
        add
        {
            Events.AddHandler(""ClickEvent"", value)
        }
        remove
        {
            Events.RemoveHandler(""ClickEvent"", value)
        }
    }
}";
                var context = new CodeGenerationContext(reuseSyntax: true);
                await TestGenerateFromSourceSymbolAsync(sourceGenerated, input, expected, onlyGenerateMembers: true, context: context);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddUnsafeEvent()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public unsafe event System.Action E;
}";
                await TestAddEventAsync(input, expected,
                    modifiers: new Editing.DeclarationModifiers(isUnsafe: true),
                    context: new CodeGenerationContext(addImports: false));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddEventWithAccessors()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public event System.Action E
    {
        add
        {
        }

        remove
        {
        }
    }
}";
                await TestAddEventAsync(input, expected,
                    addMethod: CodeGenerationSymbolFactory.CreateAccessorSymbol(ImmutableArray<AttributeData>.Empty, Accessibility.NotApplicable, ImmutableArray<SyntaxNode>.Empty),
                    removeMethod: CodeGenerationSymbolFactory.CreateAccessorSymbol(ImmutableArray<AttributeData>.Empty, Accessibility.NotApplicable, ImmutableArray<SyntaxNode>.Empty),
                    context: new CodeGenerationContext(addImports: false));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddMethodToClass()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public void M()
    {
    }
}";
                await TestAddMethodAsync(input, expected,
                    returnType: typeof(void));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddMethodToClassFromSourceSymbol()
            {
                var sourceGenerated = @"class [|C2|]
{
    public int FInt()
    {
        return 0;
    }
}";
                var input = "class [|C1|] { }";
                var expected = @"class C1
{
    public int FInt()
    {
        return 0;
    }
}";
                var context = new CodeGenerationContext(reuseSyntax: true);
                await TestGenerateFromSourceSymbolAsync(sourceGenerated, input, expected, onlyGenerateMembers: true, context: context);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddMethodToClassEscapedName()
            {
                var input = "class [|C|] { }";
                var expected = @"using System;

class C
{
    public DateTime @static()
    {
    }
}";
                await TestAddMethodAsync(input, expected,
                    name: "static",
                    returnType: typeof(DateTime));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddStaticMethodToStruct()
            {
                var input = "struct [|S|] { }";
                var expected = @"struct S
{
    public static int M()
    {
        $$
    }
}";
                await TestAddMethodAsync(input, expected,
                    modifiers: new Editing.DeclarationModifiers(isStatic: true),
                    returnType: typeof(int),
                    statements: "return 0;");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddSealedOverrideMethod()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public sealed override int GetHashCode()
    {
        $$
    }
}";
                await TestAddMethodAsync(input, expected,
                    name: "GetHashCode",
                    modifiers: new Editing.DeclarationModifiers(isOverride: true, isSealed: true),
                    returnType: typeof(int),
                    statements: "return 0;");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAbstractMethod()
            {
                var input = "abstract class [|C|] { }";
                var expected = @"abstract class C
{
    public abstract int M();
}";
                await TestAddMethodAsync(input, expected,
                    modifiers: new Editing.DeclarationModifiers(isAbstract: true),
                    returnType: typeof(int));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddMethodWithoutBody()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public int M();
}";
                await TestAddMethodAsync(input, expected,
                    returnType: typeof(int),
                    context: new CodeGenerationContext(generateMethodBodies: false));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddGenericMethod()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public int M<T>()
    {
        $$
    }
}";
                await TestAddMethodAsync(input, expected,
                    returnType: typeof(int),
                    typeParameters: ImmutableArray.Create(CodeGenerationSymbolFactory.CreateTypeParameterSymbol("T")),
                    statements: "return new T().GetHashCode();");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddVirtualMethod()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    protected virtual int M()
    {
        $$
    }
}";
                await TestAddMethodAsync(input, expected,
                    accessibility: Accessibility.Protected,
                    modifiers: new Editing.DeclarationModifiers(isVirtual: true),
                    returnType: typeof(int),
                    statements: "return 0;");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddUnsafeNewMethod()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public unsafe new string ToString()
    {
        $$
    }
}";
                await TestAddMethodAsync(input, expected,
                    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 { }";
                var expected = @"interface I { unsafe void M(int i); }
class C : I
{
    unsafe void I.M(int i)
    {
    }
}";
                await TestAddMethodAsync(input, expected,
                    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>().ToImmutableArray());
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddExplicitImplementation()
            {
                var input = "interface I { void M(int i); } class [|C|] : I { }";
                var expected = @"interface I { void M(int i); }
class C : I
{
    void I.M(int i)
    {
    }
}";
                await TestAddMethodAsync(input, expected,
                    name: "M",
                    returnType: typeof(void),
                    parameters: Parameters(Parameter(typeof(int), "i")),
                    getExplicitInterfaces: s => s.LookupSymbols(input.IndexOf('M'), null, "M").OfType<IMethodSymbol>().ToImmutableArray());
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddTrueFalseOperators()
            {
                var input = @"
class [|C|]
{
}";
                var expected = @"
class C
{
    public static bool operator true(C other)
    {
        $$
    }

    public static bool operator false(C other)
    {
        $$
    }
}";
                await TestAddOperatorsAsync(input, expected,
                    new[] { CodeGenerationOperatorKind.True, CodeGenerationOperatorKind.False },
                    parameters: Parameters(Parameter("C", "other")),
                    returnType: typeof(bool),
                    statements: "return false;");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddUnaryOperators()
            {
                var input = @"
class [|C|]
{
}";
                var expected = @"
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)
    {
        $$
    }
}";
                await TestAddOperatorsAsync(input, expected,
                    new[]
                    {
                        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 async Task AddBinaryOperators()
            {
                var input = @"
class [|C|]
{
}";
                var expected = @"
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)
    {
        $$
    }
}";
                await TestAddOperatorsAsync(input, expected,
                    new[]
                    {
                        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 async Task AddComparisonOperators()
            {
                var input = @"
class [|C|]
{
}";
                var expected = @"
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)
    {
        $$
    }
}";
                await TestAddOperatorsAsync(input, expected,
                    new[]
                    {
                        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 async Task AddUnsupportedOperator()
            {
                var input = "class [|C|] { }";
                await TestAddUnsupportedOperatorAsync(input,
                    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 async Task AddExplicitConversion()
            {
                var input = @"class [|C|] { }";
                var expected = @"class C
{
    public static explicit operator int(C other)
    {
        $$
    }
}";
                await TestAddConversionAsync(input, expected,
                    toType: typeof(int),
                    fromType: Parameter("C", "other"),
                    statements: "return 0;");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddImplicitConversion()
            {
                var input = @"class [|C|] { }";
                var expected = @"class C
{
    public static implicit operator int(C other)
    {
        $$
    }
}";
                await TestAddConversionAsync(input, expected,
                    toType: typeof(int),
                    fromType: Parameter("C", "other"),
                    isImplicit: true,
                    statements: "return 0;");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddStatements()
            {
                var input = "class C { public void [|M|]() { Console.WriteLine(1); } }";
                var expected = "class C { public void M() { Console.WriteLine(1); $$} }";
                await TestAddStatementsAsync(input, expected, "Console.WriteLine(2);");
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/840265")]
            public async Task AddDefaultParameterWithNonDefaultValueToMethod()
            {
                var input = "class C { public void [|M|]() { } }";
                var expected = "class C { public void M(string text =\"Hello\") { } }";
                await TestAddParametersAsync(input, expected,
                    Parameters(Parameter(typeof(string), "text", true, "Hello")));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddDefaultParameterWithDefaultValueToMethod()
            {
                var input = "class C { public void [|M|]() { } }";
                var expected = "class C { public void M(double number =0) { } }";
                await TestAddParametersAsync(input, expected,
                    Parameters(Parameter(typeof(double), "number", true)));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddParametersToMethod()
            {
                var input = "class C { public void [|M|]() { } }";
                var expected = "class C { public void M(int num, string text =\"Hello!\", float floating =0.5F) { } }";
                await TestAddParametersAsync(input, expected,
                    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 async Task AddParamsParameterToMethod()
            {
                var input = "class C { public void [|M|]() { } }";
                var expected = "class C { public void M(params char[]characters) { } }";
                await TestAddParametersAsync(input, expected,
                    Parameters(Parameter(typeof(char[]), "characters", isParams: true)));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544015")]
            public async Task AddAutoProperty()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public int P { get; internal set; }
}";
                await TestAddPropertyAsync(input, expected,
                    type: typeof(int),
                    setterAccessibility: Accessibility.Internal);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddUnsafeAutoProperty()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public unsafe int P { get; internal set; }
}";
                await TestAddPropertyAsync(input, expected,
                    type: typeof(int),
                    modifiers: new Editing.DeclarationModifiers(isUnsafe: true),
                    setterAccessibility: Accessibility.Internal);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddPropertyToClassFromSourceSymbol()
            {
                var sourceGenerated = @"class [|C2|]
{
    public int P
    {
        get
        {
            return 0;
        }
    }
}";
                var input = "class [|C1|] { }";
                var expected = @"class C1
{
    public int P
    {
        get
        {
            return 0;
        }
    }
}";
                var context = new CodeGenerationContext(reuseSyntax: true);
                await TestGenerateFromSourceSymbolAsync(sourceGenerated, input, expected, onlyGenerateMembers: true, context: context);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddIndexer1()
            {
                var input = "class [|C|] { }";
                var expected = "class C { public string this[int i] => String.Empty; }";
                await TestAddPropertyAsync(input, expected,
                    type: typeof(string),
                    parameters: Parameters(Parameter(typeof(int), "i")),
                    getStatements: "return String.Empty;",
                    isIndexer: true);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddIndexer2()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public string this[int i]
    {
        get
        {
            $$
        }
    }
}";
                await TestAddPropertyAsync(input, expected,
                    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 async Task AddParameterfulProperty()
            {
                var input = "class [|C|] { }";
                var expected = @"class C
{
    public string get_P(int i, int j)
    {
        $$
    }

    public void set_P(int i, int j, string value)
    {
    }
}";
                await TestAddPropertyAsync(input, expected,
                    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 async Task AddAttributeToTypes()
            {
                var input = "class [|C|] { }";
                var expected = @"[System.Serializable]
class C { }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromTypes()
            {
                var input = @"[System.Serializable] class [|C|] { }";
                var expected = "class C { }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToMethods()
            {
                var input = "class C { public void [|M()|] { } }";
                var expected = "class C { [System.Serializable] public void M() { } }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromMethods()
            {
                var input = "class C { [System.Serializable] public void [|M()|] { } }";
                var expected = "class C { public void M() { } }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToFields()
            {
                var input = "class C { [|public int F|]; }";
                var expected = "class C { [System.Serializable] public int F; }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromFields()
            {
                var input = "class C { [System.Serializable] public int [|F|]; }";
                var expected = "class C { public int F; }";
                await TestRemoveAttributeAsync<FieldDeclarationSyntax>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToProperties()
            {
                var input = "class C { public int [|P|] { get; set; }}";
                var expected = "class C { [System.Serializable] public int P { get; set; } }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromProperties()
            {
                var input = "class C { [System.Serializable] public int [|P|] { get; set; }}";
                var expected = "class C { public int P { get; set; } }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToPropertyAccessor()
            {
                var input = "class C { public int P { [|get|]; set; }}";
                var expected = "class C { public int P { [System.Serializable] get; set; }}";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromPropertyAccessor()
            {
                var input = "class C { public int P { [System.Serializable] [|get|]; set; } }";
                var expected = "class C { public int P { get; set; } }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToEnums()
            {
                var input = "enum [|C|] { One, Two }";
                var expected = @"[System.Serializable]
enum C { One, Two }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromEnums()
            {
                var input = "[System.Serializable] enum [|C|] { One, Two }";
                var expected = "enum C { One, Two }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToEnumMembers()
            {
                var input = "enum C { [|One|], Two }";
                var expected = "enum C { [System.Serializable] One, Two }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromEnumMembers()
            {
                var input = "enum C { [System.Serializable] [|One|], Two }";
                var expected = "enum C { One, Two }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToIndexer()
            {
                var input = "class C { public int [|this[int y]|] { get; set; }}";
                var expected = "class C { [System.Serializable] public int this[int y] { get; set; } }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromIndexer()
            {
                var input = "class C { [System.Serializable] public int [|this[int y]|] { get; set; }}";
                var expected = "class C { public int this[int y] { get; set; } }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToOperator()
            {
                var input = "class C { public static C operator [|+|] (C c1, C c2) { return new C(); }}";
                var expected = "class C { [System.Serializable] public static C operator +(C c1, C c2) { return new C(); } }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromOperator()
            {
                var input = "class C { [System.Serializable] public static C operator [|+|](C c1, C c2) { return new C(); }}";
                var expected = "class C { public static C operator +(C c1, C c2) { return new C(); } }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToDelegate()
            {
                var input = "delegate int [|D()|];";
                var expected = @"[System.Serializable]
delegate int D();";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromDelegate()
            {
                var input = "[System.Serializable] delegate int [|D()|];";
                var expected = "delegate int D();";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToParam()
            {
                var input = "class C { public void M([|int x|]) { } }";
                var expected = "class C { public void M([System.Serializable] int x) { } }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromParam()
            {
                var input = "class C { public void M([System.Serializable] [|int x|]) { } }";
                var expected = "class C { public void M(int x) { } }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToTypeParam()
            {
                var input = "class C<[|T|]> { }";
                var expected = "class C<[System.Serializable] T> { }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeFromTypeParam()
            {
                var input = "class C<[System.Serializable] [|T|]> { }";
                var expected = "class C<T> { }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeToCompilationUnit()
            {
                var input = "[|class C { } class D {} |]";
                var expected = @"[assembly: System.Serializable]

class C { }
class D { }";
                await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute), SyntaxFactory.Token(SyntaxKind.AssemblyKeyword));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task AddAttributeWithWrongTarget()
            {
                var input = "[|class C { } class D {} |]";
                var expected = "";
                await Assert.ThrowsAsync<AggregateException>(async () =>
                    await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute), SyntaxFactory.Token(SyntaxKind.RefKeyword)));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeWithTrivia()
            {
                // With trivia.
                var input = @"// Comment 1
[System.Serializable] // Comment 2
/* Comment 3*/ class [|C|] { }";
                var expected = @"// Comment 1
/* Comment 3*/ class C { }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeWithTrivia_NewLine()
            {
                // With trivia, redundant newline at end of attribute removed.
                var input = @"// Comment 1
[System.Serializable]
/* Comment 3*/ class [|C|] { }";
                var expected = @"// Comment 1
/* Comment 3*/ class C { }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeWithMultipleAttributes()
            {
                // Multiple attributes.
                var input = @"// Comment 1
/*Comment2*/[ /*Comment3*/ System.Serializable /*Comment4*/, /*Comment5*/System.Flags /*Comment6*/] /*Comment7*/
/* Comment 8*/
class [|C|] { }";
                var expected = @"// Comment 1
/*Comment2*/[ /*Comment3*/  /*Comment5*/System.Flags /*Comment6*/] /*Comment7*/
/* Comment 8*/
class C { }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task RemoveAttributeWithMultipleAttributeLists()
            {
                // Multiple attributes.
                var input = @"// Comment 1
/*Comment2*/[ /*Comment3*/ System.Serializable /*Comment4*/, /*Comment5*/System.Flags /*Comment6*/] /*Comment7*/
[ /*Comment9*/ System.Obsolete /*Comment10*/] /*Comment11*/
/* Comment12*/
class [|C|] { }";
                var expected = @"// Comment 1
/*Comment2*/[ /*Comment3*/  /*Comment5*/System.Flags /*Comment6*/] /*Comment7*/
[ /*Comment9*/ System.Obsolete /*Comment10*/] /*Comment11*/
/* Comment12*/
class C { }";
                await TestRemoveAttributeAsync<SyntaxNode>(input, expected, typeof(SerializableAttribute));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task TestUpdateModifiers()
            {
                var input = @"public static class [|C|] // Comment 1
{
    // Comment 2
}";
                var expected = @"internal partial sealed class C // Comment 1
{
    // Comment 2
}";
                var eol = SyntaxFactory.EndOfLine(@"");
                var newModifiers = new[] { SyntaxFactory.Token(SyntaxKind.InternalKeyword).WithLeadingTrivia(eol) }.Concat(
                    CreateModifierTokens(new Editing.DeclarationModifiers(isSealed: true, isPartial: true), LanguageNames.CSharp));

                await TestUpdateDeclarationAsync<ClassDeclarationSyntax>(input, expected, modifiers: newModifiers);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task TestUpdateAccessibility()
            {
                var input = @"// Comment 0
public static class [|C|] // Comment 1
{
    // Comment 2
}";
                var expected = @"// Comment 0
internal static class C // Comment 1
{
    // Comment 2
}";
                await TestUpdateDeclarationAsync<ClassDeclarationSyntax>(input, expected, accessibility: Accessibility.Internal);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task TestUpdateDeclarationType()
            {
                var input = @"
public static class C
{
    // Comment 1
    public static char [|F|]() { return 0; }
}";
                var expected = @"
public static class C
{
    // Comment 1
    public static int F() { return 0; }
}";
                await TestUpdateDeclarationAsync<MethodDeclarationSyntax>(input, expected, getType: GetTypeSymbol(typeof(int)));
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task TestUpdateDeclarationMembers()
            {
                var input = @"
public static class [|C|]
{
    // Comment 0
    public int {|RetainedMember:f|};

    // Comment 1
    public static char F() { return 0; }
}";
                var expected = @"
public static class C
{
    // Comment 0
    public int f;
    public int f2;
}";
                var getField = CreateField(Accessibility.Public, new Editing.DeclarationModifiers(), typeof(int), "f2");
                var getMembers = ImmutableArray.Create(getField);
                await TestUpdateDeclarationAsync<ClassDeclarationSyntax>(
                    input, expected, getNewMembers: getMembers);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)]
            public async Task TestUpdateDeclarationMembers_DifferentOrder()
            {
                var input = @"
public static class [|C|]
{
    // Comment 0
    public int f;

    // Comment 1
    public static char {|RetainedMember:F|}() { return 0; }
}";
                var expected = @"
public static class C
{
    public int f2;

    // Comment 1
    public static char F() { return 0; }
}";
                var getField = CreateField(Accessibility.Public, new Editing.DeclarationModifiers(), typeof(int), "f2");
                var getMembers = ImmutableArray.Create(getField);
                await TestUpdateDeclarationAsync<ClassDeclarationSyntax>(input, expected, getNewMembers: getMembers, declareNewMembersAtTop: true);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGenerationSortDeclarations)]
            public async Task SortAroundDestructor()
            {
                var generationSource = "public class [|C|] { public C(){} public int this[int index]{get{return 0;}set{value = 0;}} }";
                var initial = "public class [|C|] { ~C(){} }";
                var expected = @"public class C {
    public C()
    {
    }

    ~C(){}

    public int this[int index]
    {
        get
        {
        }

        set
        {
        }
    }
}";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected, onlyGenerateMembers: true);
            }

            [Fact, Trait(Traits.Feature, Traits.Features.CodeGenerationSortDeclarations)]
            public async Task SortOperators()
            {
                var generationSource = @"
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; }
    }
}";
                var initial = "namespace [|N|] { }";
                var expected = @"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);
    }
}";
                await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected,
                    forceLanguage: LanguageNames.CSharp,
                    context: new CodeGenerationContext(generateMethodBodies: false));
            }
        }

        [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/665008")]
        public async Task TestExtensionMethods()
        {
            var generationSource = @"
public static class [|C|]
{
    public static void ExtMethod1(this string s, int y, string z) {}
}";
            var initial = "public static class [|C|] {}";
            var expected = @"public static class C
{
    public static void ExtMethod1(this string s, int y, string z);
}";
            await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected,
                context: new CodeGenerationContext(generateMethodBodies: false),
                onlyGenerateMembers: true);
        }

        [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530829")]
        public async Task TestVBPropertiesWithParams()
        {
            var generationSource = @"
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
";

            var initial = "namespace [|N|] {}";
            var expected = @"namespace N
{
    public class C
    {
        public virtual string get_IndexProp(int p1);
        public virtual void set_IndexProp(int p1, string value);
    }
}";
            await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected,
                context: new CodeGenerationContext(generateMethodBodies: false));
        }

        [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/812738")]
        public async Task TestRefParamsWithDefaultValue()
        {
            var generationSource = @"
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";
            var initial = "public class [|C|] {}";
            var expected = @"public class C
{
    public void Goo(int x, ref int y, ref object z);
}";
            await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected,
                context: new CodeGenerationContext(generateMethodBodies: false),
                onlyGenerateMembers: true);
        }

        [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/848357")]
        public async Task TestConstraints()
        {
            var generationSource = @"
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;
    }
}
";
            var initial = "namespace [|N|] {}";
            var expected = @"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;
    }
}";
            await TestGenerateFromSourceSymbolAsync(generationSource, initial, expected,
                context: new CodeGenerationContext(generateMethodBodies: false),
                onlyGenerateMembers: true);
        }
    }
}
