File: CodeGen\IndexerTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Emit\Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Emit.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.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
    public class IndexerTests : CSharpTestBase
    {
        #region Declarations
 
        [Fact]
        public void ReadWriteIndexer()
        {
            var text = @"
class C
{
    public int this[int x] { get { return x; } set { } }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateIndexer(module, "System.Int32 C.this[System.Int32 x].get", "void C.this[System.Int32 x].set"),
                expectedSignatures: new[]
                {
                    Signature("C", "Item", ".property readwrite instance System.Int32 Item(System.Int32 x)"),
                    Signature("C", "get_Item", ".method public hidebysig specialname instance System.Int32 get_Item(System.Int32 x) cil managed"),
                    Signature("C", "set_Item", ".method public hidebysig specialname instance System.Void set_Item(System.Int32 x, System.Int32 value) cil managed")
                });
        }
 
        [Fact]
        public void ReadOnlyIndexer()
        {
            var text = @"
class C
{
    public int this[int x, int y] { get { return x; } }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateIndexer(module, "System.Int32 C.this[System.Int32 x, System.Int32 y].get", null),
                expectedSignatures: new[]
                {
                    Signature("C", "Item", ".property readonly instance System.Int32 Item(System.Int32 x, System.Int32 y)"),
                    Signature("C", "get_Item", ".method public hidebysig specialname instance System.Int32 get_Item(System.Int32 x, System.Int32 y) cil managed")
                });
        }
 
        [Fact]
        public void WriteOnlyIndexer()
        {
            var text = @"
class C
{
    public int this[int x, int y, int z] { set { } }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateIndexer(module, null, "void C.this[System.Int32 x, System.Int32 y, System.Int32 z].set"),
                expectedSignatures: new[]
                {
                    Signature("C", "Item", ".property writeonly instance System.Int32 Item(System.Int32 x, System.Int32 y, System.Int32 z)"),
                    Signature("C", "set_Item", ".method public hidebysig specialname instance System.Void set_Item(System.Int32 x, System.Int32 y, System.Int32 z, System.Int32 value) cil managed")
                });
        }
 
        [Fact]
        public void GenericIndexer()
        {
            var text = @"
class C<T>
{
    public T this[T x] { get { return x; } set { } }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateIndexer(module, "T C<T>.this[T x].get", "void C<T>.this[T x].set"),
                expectedSignatures: new[]
                {
                    Signature("C`1", "Item", ".property readwrite instance T Item(T x)"),
                    Signature("C`1", "get_Item", ".method public hidebysig specialname instance T get_Item(T x) cil managed"),
                    Signature("C`1", "set_Item", ".method public hidebysig specialname instance System.Void set_Item(T x, T value) cil managed")
                });
        }
 
        [Fact]
        public void IndexerWithOptionalParameters()
        {
            var text = @"
class C
{
    public int this[int x = 1, int y = 2] { get { return x; } set { } }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateIndexer(module, "System.Int32 C.this[[System.Int32 x = 1], [System.Int32 y = 2]].get", "void C.this[[System.Int32 x = 1], [System.Int32 y = 2]].set"),
                expectedSignatures: new[]
                {
                    Signature("C", "Item", ".property readwrite instance System.Int32 Item([opt] System.Int32 x = 1, [opt] System.Int32 y = 2)"),
                    Signature("C", "get_Item", ".method public hidebysig specialname instance System.Int32 get_Item([opt] System.Int32 x = 1, [opt] System.Int32 y = 2) cil managed"),
                    Signature("C", "set_Item", ".method public hidebysig specialname instance System.Void set_Item([opt] System.Int32 x = 1, [opt] System.Int32 y = 2, System.Int32 value) cil managed")
                });
        }
 
        [Fact]
        public void IndexerWithParameterArray()
        {
            var text = @"
class C
{
    public int this[params int[] x] { get { return 0; } set { } }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateIndexer(module, "System.Int32 C.this[params System.Int32[] x].get", "void C.this[params System.Int32[] x].set"),
                expectedSignatures: new[]
                {
                    Signature("C", "Item", ".property readwrite instance System.Int32 Item([System.ParamArrayAttribute()] System.Int32[] x)"),
                    Signature("C", "get_Item", ".method public hidebysig specialname instance System.Int32 get_Item([System.ParamArrayAttribute()] System.Int32[] x) cil managed"),
                    Signature("C", "set_Item", ".method public hidebysig specialname instance System.Void set_Item([System.ParamArrayAttribute()] System.Int32[] x, System.Int32 value) cil managed")
                });
        }
 
        [Fact]
        public void ExplicitInterfaceImplementation()
        {
            var text = @"
interface I
{
    int this[int x] { get; set; }
}
 
class C : I
{
    int I.this[int x] { get { return 0; } set { } }
}
";
            System.Action<ModuleSymbol> validator = module =>
            {
                // Can't use ValidateIndexer because explicit implementations aren't indexers in metadata.
 
                var @class = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
                var indexer = @class.GetMembers().Where(member => member.Kind == SymbolKind.Property).Cast<PropertySymbol>().Single();
 
                Assert.False(indexer.IsIndexer);
                Assert.True(indexer.MustCallMethodsDirectly); //since has parameters, but isn't an indexer
                Assert.Equal(Accessibility.Private, indexer.DeclaredAccessibility);
                Assert.False(indexer.IsStatic);
 
                var getMethod = indexer.GetMethod;
                Assert.Equal(MethodKind.ExplicitInterfaceImplementation, getMethod.MethodKind); //since CallMethodsDirectly
                Assert.Equal("System.Int32 C.I.get_Item(System.Int32 x)", getMethod.ToTestDisplayString());
                getMethod.CheckAccessorModifiers(indexer);
 
                var setMethod = indexer.SetMethod;
                Assert.Equal(MethodKind.ExplicitInterfaceImplementation, setMethod.MethodKind); //since CallMethodsDirectly
                Assert.Equal("void C.I.set_Item(System.Int32 x, System.Int32 value)", setMethod.ToTestDisplayString());
                setMethod.CheckAccessorModifiers(indexer);
            };
            var compVerifier = CompileAndVerify(text, symbolValidator: validator, expectedSignatures: new[]
            {
                Signature("C", "I.Item", ".property readwrite System.Int32 I.Item(System.Int32 x)"),
                Signature("C", "I.get_Item", ".method private hidebysig newslot specialname virtual final instance System.Int32 I.get_Item(System.Int32 x) cil managed"),
                Signature("C", "I.set_Item", ".method private hidebysig newslot specialname virtual final instance System.Void I.set_Item(System.Int32 x, System.Int32 value) cil managed")
            });
        }
 
        [Fact]
        public void ImplicitInterfaceImplementation()
        {
            var text = @"
interface I
{
    int this[int x] { get; set; }
}
 
class C : I
{
    public int this[int x] { get { return 0; } set { } }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateIndexer(module, "System.Int32 C.this[System.Int32 x].get", "void C.this[System.Int32 x].set"),
                expectedSignatures: new[]
                {
                    Signature("C", "Item", ".property readwrite instance System.Int32 Item(System.Int32 x)"),
                    Signature("C", "get_Item", ".method public hidebysig newslot specialname virtual final instance System.Int32 get_Item(System.Int32 x) cil managed"),
                    Signature("C", "set_Item", ".method public hidebysig newslot specialname virtual final instance System.Void set_Item(System.Int32 x, System.Int32 value) cil managed")
                });
        }
 
        [Fact]
        public void Overriding()
        {
            var text = @"
class B
{
    public virtual int this[int x] { get { return 0; } set { } }
}
 
class C : B
{
    public override sealed int this[int x] { get { return 0; } set { } }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateIndexer(module, "System.Int32 C.this[System.Int32 x].get", "void C.this[System.Int32 x].set"),
                expectedSignatures: new[]
                {
                    Signature("C", "Item", ".property readwrite instance System.Int32 Item(System.Int32 x)"),
                    Signature("C", "get_Item", ".method public hidebysig specialname virtual final instance System.Int32 get_Item(System.Int32 x) cil managed"),
                    Signature("C", "set_Item", ".method public hidebysig specialname virtual final instance System.Void set_Item(System.Int32 x, System.Int32 value) cil managed")
                });
        }
 
        [Fact]
        public void Hiding()
        {
            var text = @"
class B
{
    public virtual int this[int x] { get { return 0; } set { } }
}
 
class C : B
{
    public new virtual int this[int x] { get { return 0; } set { } }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateIndexer(module, "System.Int32 C.this[System.Int32 x].get", "void C.this[System.Int32 x].set"),
                expectedSignatures: new[]
                {
                    Signature("C", "Item", ".property readwrite instance System.Int32 Item(System.Int32 x)"),
                    Signature("C", "get_Item", ".method public hidebysig newslot specialname virtual instance System.Int32 get_Item(System.Int32 x) cil managed"),
                    Signature("C", "set_Item", ".method public hidebysig newslot specialname virtual instance System.Void set_Item(System.Int32 x, System.Int32 value) cil managed")
                });
        }
 
        // NOTE: assumes there's a single indexer (type = int) in a type C.
        private static void ValidateIndexer(ModuleSymbol module, string getterDisplayString, string setterDisplayString)
        {
            var @class = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var indexer = @class.Indexers.Single();
 
            Assert.Equal(SymbolKind.Property, indexer.Kind);
            Assert.True(indexer.IsIndexer);
            Assert.False(indexer.MustCallMethodsDirectly);
            Assert.Equal(Accessibility.Public, indexer.DeclaredAccessibility);
            Assert.False(indexer.IsStatic);
 
            var getMethod = indexer.GetMethod;
            if (getterDisplayString == null)
            {
                Assert.Null(getMethod);
            }
            else
            {
                Assert.Equal(MethodKind.PropertyGet, getMethod.MethodKind);
                Assert.Equal(getterDisplayString, getMethod.ToTestDisplayString());
                getMethod.CheckAccessorShape(indexer);
            }
 
            var setMethod = indexer.SetMethod;
            if (setterDisplayString == null)
            {
                Assert.Null(setMethod);
            }
            else
            {
                Assert.Equal(MethodKind.PropertySet, setMethod.MethodKind);
                Assert.Equal(setterDisplayString, setMethod.ToTestDisplayString());
                setMethod.CheckAccessorShape(indexer);
            }
        }
 
        #endregion Declarations
 
        #region Lowering
 
        private const string TypeWithIndexers = @"
public class C
{
    public static int Goo(int x)
    {
        System.Console.Write(x + "","");
        return x * 10;
    }
 
    public int this[int x, int y = 9]
    {
        get
        {
            System.Console.Write(x + "","");
            System.Console.Write(y + "","");
            return -(x + y);
        }
        set
        {
            System.Console.Write(x + "","");
            System.Console.Write(y + "","");
            System.Console.Write(value + "","");
        }
    }
 
    public int this[params int[] x]
    {
        get
        {
            foreach (var i in x) System.Console.Write(i + "","");
            return -(x.Length);
        }
        set
        {
            foreach (var i in x) System.Console.Write(i + "","");
            System.Console.Write(value + "","");
        }
    }
}
";
 
        [Fact]
        public void LoweringRead()
        {
            var text = TypeWithIndexers + @"
class Test
{
    static void Main()
    {
        C c = new C();
        int x;
 
        //// Normal
 
        x = c[C.Goo(1), C.Goo(2)];
        System.Console.WriteLine();
 
        //// Named parameters
 
        x = c[C.Goo(1), y: C.Goo(2)]; //NB: Dev10 gets this wrong (2,1,10,20)
        System.Console.WriteLine();
 
        x = c[x: C.Goo(1), y: C.Goo(2)];
        System.Console.WriteLine();
 
        x = c[y: C.Goo(2), x: C.Goo(1)];
        System.Console.WriteLine();
 
        //// Optional parameters
 
        x = c[C.Goo(1)];
        System.Console.WriteLine();
 
        x = c[x: C.Goo(1)];
        System.Console.WriteLine();
 
        //// Parameter arrays
 
        x = c[C.Goo(1), C.Goo(2), C.Goo(3)];
        System.Console.WriteLine();
 
        x = c[new int[] { C.Goo(1), C.Goo(2), C.Goo(3) }];
        System.Console.WriteLine();
    }
}
";
            CompileAndVerify(text, expectedOutput: @"
1,2,10,20,
1,2,10,20,
1,2,10,20,
2,1,10,20,
1,10,9,
1,10,9,
1,2,3,10,20,30,
1,2,3,10,20,30,
");
        }
 
        [Fact]
        public void LoweringReadIL()
        {
            var text = TypeWithIndexers + @"
class Test
{
    static void Main()
    {
        C c = new C();
        int x;
 
        //// Normal
 
        x = c[1, 2];
 
        //// Named parameters
 
        x = c[1, y: 2];
        x = c[x: 1, y: 2];
        x = c[y: 2, x: 1];
 
        //// Optional parameters
 
        x = c[1];
        x = c[x: 1];
 
        //// Parameter arrays
 
        x = c[1, 2, 3];
        x = c[new int[] { 1, 2, 3 }];
    }
}
";
            var compVerifier = CompileAndVerify(text, options: TestOptions.ReleaseExe.WithModuleName("MODULE"));
 
            compVerifier.VerifyIL("Test.Main", @"
{
  // Code size      111 (0x6f)
  .maxstack  4
  .locals init (C V_0) //c
  IL_0000:  newobj     ""C..ctor()""
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  ldc.i4.1
  IL_0008:  ldc.i4.2
  IL_0009:  callvirt   ""int C.this[int, int].get""
  IL_000e:  pop
  IL_000f:  ldloc.0
  IL_0010:  ldc.i4.1
  IL_0011:  ldc.i4.2
  IL_0012:  callvirt   ""int C.this[int, int].get""
  IL_0017:  pop
  IL_0018:  ldloc.0
  IL_0019:  ldc.i4.1
  IL_001a:  ldc.i4.2
  IL_001b:  callvirt   ""int C.this[int, int].get""
  IL_0020:  pop
  IL_0021:  ldloc.0
  IL_0022:  ldc.i4.1
  IL_0023:  ldc.i4.2
  IL_0024:  callvirt   ""int C.this[int, int].get""
  IL_0029:  pop
  IL_002a:  ldloc.0
  IL_002b:  ldc.i4.1
  IL_002c:  ldc.i4.s   9
  IL_002e:  callvirt   ""int C.this[int, int].get""
  IL_0033:  pop
  IL_0034:  ldloc.0
  IL_0035:  ldc.i4.1
  IL_0036:  ldc.i4.s   9
  IL_0038:  callvirt   ""int C.this[int, int].get""
  IL_003d:  pop
  IL_003e:  ldloc.0
  IL_003f:  ldc.i4.3
  IL_0040:  newarr     ""int""
  IL_0045:  dup
  IL_0046:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_004b:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0050:  callvirt   ""int C.this[params int[]].get""
  IL_0055:  pop
  IL_0056:  ldloc.0
  IL_0057:  ldc.i4.3
  IL_0058:  newarr     ""int""
  IL_005d:  dup
  IL_005e:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_0063:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0068:  callvirt   ""int C.this[params int[]].get""
  IL_006d:  pop
  IL_006e:  ret
}
");
        }
 
        [Fact]
        public void LoweringAssignment()
        {
            var text = TypeWithIndexers + @"
class Test
{
    static void Main()
    {
        C c = new C();
 
        //// Normal
 
        c[C.Goo(1), C.Goo(2)] = C.Goo(3);
        System.Console.WriteLine();
 
        //// Named parameters
 
        c[C.Goo(1), y: C.Goo(2)] = C.Goo(3);
        System.Console.WriteLine();
 
        c[x: C.Goo(1), y: C.Goo(2)] = C.Goo(3); //NB: dev10 gets this wrong (2,1,3,10,20,30,)
        System.Console.WriteLine();
 
        c[y: C.Goo(2), x: C.Goo(1)] = C.Goo(3);
        System.Console.WriteLine();
 
        //// Optional parameters
 
        c[C.Goo(1)] = C.Goo(3);
        System.Console.WriteLine();
 
        c[x: C.Goo(1)] = C.Goo(3);
        System.Console.WriteLine();
 
        //// Parameter arrays
 
        c[C.Goo(1), C.Goo(2), C.Goo(3)] = C.Goo(4);
        System.Console.WriteLine();
 
        c[new int[] { C.Goo(1), C.Goo(2), C.Goo(3) }] = C.Goo(4);
        System.Console.WriteLine();
    }
}
";
            CompileAndVerify(text, expectedOutput: @"
1,2,3,10,20,30,
1,2,3,10,20,30,
1,2,3,10,20,30,
2,1,3,10,20,30,
1,3,10,9,30,
1,3,10,9,30,
1,2,3,4,10,20,30,40,
1,2,3,4,10,20,30,40,
");
        }
 
        [Fact]
        public void LoweringAssignmentIL()
        {
            var text = TypeWithIndexers + @"
class Test
{
    static void Main()
    {
        C c = new C();
 
        //// Normal
 
        c[1, 2] = 3;
 
        //// Named parameters
 
        c[1, y: 2] = 3;
        c[x: 1, y: 2] = 3;
        c[y: 2, x: 1] = 3;
 
        //// Optional parameters
 
        c[1] = 3;
        c[x: 1] = 3;
 
        //// Parameter arrays
 
        c[1, 2, 3] = 4;
        c[new int[] { 1, 2, 3 }] = 4;
    }
}
";
            var compVerifier = CompileAndVerify(text, options: TestOptions.ReleaseExe.WithModuleName("MODULE"));
 
            compVerifier.VerifyIL("Test.Main", @"
{
  // Code size      111 (0x6f)
  .maxstack  4
  .locals init (C V_0) //c
  IL_0000:  newobj     ""C..ctor()""
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  ldc.i4.1
  IL_0008:  ldc.i4.2
  IL_0009:  ldc.i4.3
  IL_000a:  callvirt   ""void C.this[int, int].set""
  IL_000f:  ldloc.0
  IL_0010:  ldc.i4.1
  IL_0011:  ldc.i4.2
  IL_0012:  ldc.i4.3
  IL_0013:  callvirt   ""void C.this[int, int].set""
  IL_0018:  ldloc.0
  IL_0019:  ldc.i4.1
  IL_001a:  ldc.i4.2
  IL_001b:  ldc.i4.3
  IL_001c:  callvirt   ""void C.this[int, int].set""
  IL_0021:  ldloc.0
  IL_0022:  ldc.i4.1
  IL_0023:  ldc.i4.2
  IL_0024:  ldc.i4.3
  IL_0025:  callvirt   ""void C.this[int, int].set""
  IL_002a:  ldloc.0
  IL_002b:  ldc.i4.1
  IL_002c:  ldc.i4.s   9
  IL_002e:  ldc.i4.3
  IL_002f:  callvirt   ""void C.this[int, int].set""
  IL_0034:  ldloc.0
  IL_0035:  ldc.i4.1
  IL_0036:  ldc.i4.s   9
  IL_0038:  ldc.i4.3
  IL_0039:  callvirt   ""void C.this[int, int].set""
  IL_003e:  ldloc.0
  IL_003f:  ldc.i4.3
  IL_0040:  newarr     ""int""
  IL_0045:  dup
  IL_0046:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_004b:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0050:  ldc.i4.4
  IL_0051:  callvirt   ""void C.this[params int[]].set""
  IL_0056:  ldloc.0
  IL_0057:  ldc.i4.3
  IL_0058:  newarr     ""int""
  IL_005d:  dup
  IL_005e:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_0063:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0068:  ldc.i4.4
  IL_0069:  callvirt   ""void C.this[params int[]].set""
  IL_006e:  ret
}
");
        }
 
        [Fact]
        public void LoweringIncrement()
        {
            var text = TypeWithIndexers + @"
class Test
{
    static void Main()
    {
        C c = new C();
 
        //// Normal
 
        c[C.Goo(1), C.Goo(2)]++;
        System.Console.WriteLine();
 
        //// Named parameters
 
        c[C.Goo(1), y: C.Goo(2)]++;
        System.Console.WriteLine();
 
        c[x: C.Goo(1), y: C.Goo(2)]++; //NB: dev10 gets this wrong (2,1,10,20,10,20,-29,)
        System.Console.WriteLine();
 
        c[y: C.Goo(2), x: C.Goo(1)]++;
        System.Console.WriteLine();
 
        //// Optional parameters
 
        c[C.Goo(1)]++;
        System.Console.WriteLine();
 
        c[x: C.Goo(1)]++;
        System.Console.WriteLine();
 
        //// Parameter arrays
 
        c[C.Goo(1), C.Goo(2), C.Goo(3)]++;
        System.Console.WriteLine();
 
        c[new int[] { C.Goo(1), C.Goo(2), C.Goo(3) }]++;
        System.Console.WriteLine();
    }
}
";
            var compVerifier = CompileAndVerify(text, expectedOutput: @"
1,2,10,20,10,20,-29,
1,2,10,20,10,20,-29,
1,2,10,20,10,20,-29,
2,1,10,20,10,20,-29,
1,10,9,10,9,-18,
1,10,9,10,9,-18,
1,2,3,10,20,30,10,20,30,-2,
1,2,3,10,20,30,10,20,30,-2,
");
        }
 
        [Fact]
        public void LoweringIncrementIL()
        {
            var text = TypeWithIndexers + @"
class Test
{
    static void Main()
    {
        C c = new C();
 
        //// Normal
 
        c[1, 2]++;
 
        //// Named parameters
 
        c[1, y: 2]++;
        c[x: 1, y: 2]++;
        c[y: 2, x: 1]++;
 
        //// Optional parameters
 
        c[1]++;
        c[x: 1]++;
 
        //// Parameter arrays
 
        c[1, 2, 3]++;
        c[new int[] { 1, 2, 3 }]++;
    }
}
";
            var compVerifier = CompileAndVerify(text, options: TestOptions.ReleaseExe.WithModuleName("MODULE"));
 
            compVerifier.VerifyIL("Test.Main", @"
{
  // Code size      207 (0xcf)
  .maxstack  5
  .locals init (C V_0, //c
                int V_1,
                C V_2,
                int[] V_3)
  IL_0000:  newobj     ""C..ctor()""
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  dup
  IL_0008:  ldc.i4.1
  IL_0009:  ldc.i4.2
  IL_000a:  callvirt   ""int C.this[int, int].get""
  IL_000f:  stloc.1
  IL_0010:  ldc.i4.1
  IL_0011:  ldc.i4.2
  IL_0012:  ldloc.1
  IL_0013:  ldc.i4.1
  IL_0014:  add
  IL_0015:  callvirt   ""void C.this[int, int].set""
  IL_001a:  ldloc.0
  IL_001b:  dup
  IL_001c:  ldc.i4.1
  IL_001d:  ldc.i4.2
  IL_001e:  callvirt   ""int C.this[int, int].get""
  IL_0023:  stloc.1
  IL_0024:  ldc.i4.1
  IL_0025:  ldc.i4.2
  IL_0026:  ldloc.1
  IL_0027:  ldc.i4.1
  IL_0028:  add
  IL_0029:  callvirt   ""void C.this[int, int].set""
  IL_002e:  ldloc.0
  IL_002f:  dup
  IL_0030:  ldc.i4.1
  IL_0031:  ldc.i4.2
  IL_0032:  callvirt   ""int C.this[int, int].get""
  IL_0037:  stloc.1
  IL_0038:  ldc.i4.1
  IL_0039:  ldc.i4.2
  IL_003a:  ldloc.1
  IL_003b:  ldc.i4.1
  IL_003c:  add
  IL_003d:  callvirt   ""void C.this[int, int].set""
  IL_0042:  ldloc.0
  IL_0043:  dup
  IL_0044:  ldc.i4.1
  IL_0045:  ldc.i4.2
  IL_0046:  callvirt   ""int C.this[int, int].get""
  IL_004b:  stloc.1
  IL_004c:  ldc.i4.1
  IL_004d:  ldc.i4.2
  IL_004e:  ldloc.1
  IL_004f:  ldc.i4.1
  IL_0050:  add
  IL_0051:  callvirt   ""void C.this[int, int].set""
  IL_0056:  ldloc.0
  IL_0057:  dup
  IL_0058:  ldc.i4.1
  IL_0059:  ldc.i4.s   9
  IL_005b:  callvirt   ""int C.this[int, int].get""
  IL_0060:  stloc.1
  IL_0061:  ldc.i4.1
  IL_0062:  ldc.i4.s   9
  IL_0064:  ldloc.1
  IL_0065:  ldc.i4.1
  IL_0066:  add
  IL_0067:  callvirt   ""void C.this[int, int].set""
  IL_006c:  ldloc.0
  IL_006d:  dup
  IL_006e:  ldc.i4.1
  IL_006f:  ldc.i4.s   9
  IL_0071:  callvirt   ""int C.this[int, int].get""
  IL_0076:  stloc.1
  IL_0077:  ldc.i4.1
  IL_0078:  ldc.i4.s   9
  IL_007a:  ldloc.1
  IL_007b:  ldc.i4.1
  IL_007c:  add
  IL_007d:  callvirt   ""void C.this[int, int].set""
  IL_0082:  ldloc.0
  IL_0083:  stloc.2
  IL_0084:  ldc.i4.3
  IL_0085:  newarr     ""int""
  IL_008a:  dup
  IL_008b:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_0090:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0095:  stloc.3
  IL_0096:  ldloc.2
  IL_0097:  ldloc.3
  IL_0098:  callvirt   ""int C.this[params int[]].get""
  IL_009d:  stloc.1
  IL_009e:  ldloc.2
  IL_009f:  ldloc.3
  IL_00a0:  ldloc.1
  IL_00a1:  ldc.i4.1
  IL_00a2:  add
  IL_00a3:  callvirt   ""void C.this[params int[]].set""
  IL_00a8:  ldloc.0
  IL_00a9:  stloc.2
  IL_00aa:  ldc.i4.3
  IL_00ab:  newarr     ""int""
  IL_00b0:  dup
  IL_00b1:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_00b6:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_00bb:  stloc.3
  IL_00bc:  ldloc.2
  IL_00bd:  ldloc.3
  IL_00be:  callvirt   ""int C.this[params int[]].get""
  IL_00c3:  stloc.1
  IL_00c4:  ldloc.2
  IL_00c5:  ldloc.3
  IL_00c6:  ldloc.1
  IL_00c7:  ldc.i4.1
  IL_00c8:  add
  IL_00c9:  callvirt   ""void C.this[params int[]].set""
  IL_00ce:  ret
}
");
        }
 
        [Fact]
        public void LoweringCompoundAssignment()
        {
            var text = TypeWithIndexers + @"
class Test
{
    static void Main()
    {
        C c = new C();
 
        //// Normal
 
        c[C.Goo(1), C.Goo(2)] += C.Goo(3);
        System.Console.WriteLine();
 
        //// Named parameters
 
        c[C.Goo(1), y: C.Goo(2)] += C.Goo(3);
        System.Console.WriteLine();
 
        c[x: C.Goo(1), y: C.Goo(2)] += C.Goo(3); //NB: dev10 gets this wrong (2,1,10,20,3,10,20,0,)
        System.Console.WriteLine();
 
        c[y: C.Goo(2), x: C.Goo(1)] += C.Goo(3);
        System.Console.WriteLine();
 
        //// Optional parameters
 
        c[C.Goo(1)] += C.Goo(3);
        System.Console.WriteLine();
 
        c[x: C.Goo(1)] += C.Goo(3);
        System.Console.WriteLine();
 
        //// Parameter arrays
 
        c[C.Goo(1), C.Goo(2), C.Goo(3)] += C.Goo(4);
        System.Console.WriteLine();
 
        c[new int[] { C.Goo(1), C.Goo(2), C.Goo(3) }] += C.Goo(4);
        System.Console.WriteLine();
    }
}
";
            CompileAndVerify(text, expectedOutput: @"
1,2,10,20,3,10,20,0,
1,2,10,20,3,10,20,0,
1,2,10,20,3,10,20,0,
2,1,10,20,3,10,20,0,
1,10,9,3,10,9,11,
1,10,9,3,10,9,11,
1,2,3,10,20,30,4,10,20,30,37,
1,2,3,10,20,30,4,10,20,30,37,
");
        }
 
        [Fact]
        public void LoweringCompoundAssignmentIL()
        {
            var text = TypeWithIndexers + @"
class Test
{
    static void Main()
    {
        C c = new C();
 
        //// Normal
 
        c[1, 2] += 3;
        System.Console.WriteLine();
 
        //// Named parameters
 
        c[1, y: 2] += 3;
        System.Console.WriteLine();
 
        c[x: 1, y: 2] += 3; //NB: dev10 gets this wrong (2,1,10,20,3,10,20,0,)
        System.Console.WriteLine();
 
        c[y: 2, x: 1] += 3;
        System.Console.WriteLine();
 
        //// Optional parameters
 
        c[1] += 3;
        System.Console.WriteLine();
 
        c[x: 1] += 3;
        System.Console.WriteLine();
 
        //// Parameter arrays
 
        c[1, 2, 3] += 4;
        System.Console.WriteLine();
 
        c[new int[] { 1, 2, 3 }] += 4;
        System.Console.WriteLine();
    }
}
";
            var compVerifier = CompileAndVerify(text, options: TestOptions.ReleaseExe.WithModuleName("MODULE"));
 
            compVerifier.VerifyIL("Test.Main", @"
{
  // Code size      243 (0xf3)
  .maxstack  6
  .locals init (C V_0, //c
                C V_1,
                int[] V_2)
  IL_0000:  newobj     ""C..ctor()""
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  stloc.1
  IL_0008:  ldloc.1
  IL_0009:  ldc.i4.1
  IL_000a:  ldc.i4.2
  IL_000b:  ldloc.1
  IL_000c:  ldc.i4.1
  IL_000d:  ldc.i4.2
  IL_000e:  callvirt   ""int C.this[int, int].get""
  IL_0013:  ldc.i4.3
  IL_0014:  add
  IL_0015:  callvirt   ""void C.this[int, int].set""
  IL_001a:  call       ""void System.Console.WriteLine()""
  IL_001f:  ldloc.0
  IL_0020:  stloc.1
  IL_0021:  ldloc.1
  IL_0022:  ldc.i4.1
  IL_0023:  ldc.i4.2
  IL_0024:  ldloc.1
  IL_0025:  ldc.i4.1
  IL_0026:  ldc.i4.2
  IL_0027:  callvirt   ""int C.this[int, int].get""
  IL_002c:  ldc.i4.3
  IL_002d:  add
  IL_002e:  callvirt   ""void C.this[int, int].set""
  IL_0033:  call       ""void System.Console.WriteLine()""
  IL_0038:  ldloc.0
  IL_0039:  stloc.1
  IL_003a:  ldloc.1
  IL_003b:  ldc.i4.1
  IL_003c:  ldc.i4.2
  IL_003d:  ldloc.1
  IL_003e:  ldc.i4.1
  IL_003f:  ldc.i4.2
  IL_0040:  callvirt   ""int C.this[int, int].get""
  IL_0045:  ldc.i4.3
  IL_0046:  add
  IL_0047:  callvirt   ""void C.this[int, int].set""
  IL_004c:  call       ""void System.Console.WriteLine()""
  IL_0051:  ldloc.0
  IL_0052:  stloc.1
  IL_0053:  ldloc.1
  IL_0054:  ldc.i4.1
  IL_0055:  ldc.i4.2
  IL_0056:  ldloc.1
  IL_0057:  ldc.i4.1
  IL_0058:  ldc.i4.2
  IL_0059:  callvirt   ""int C.this[int, int].get""
  IL_005e:  ldc.i4.3
  IL_005f:  add
  IL_0060:  callvirt   ""void C.this[int, int].set""
  IL_0065:  call       ""void System.Console.WriteLine()""
  IL_006a:  ldloc.0
  IL_006b:  stloc.1
  IL_006c:  ldloc.1
  IL_006d:  ldc.i4.1
  IL_006e:  ldc.i4.s   9
  IL_0070:  ldloc.1
  IL_0071:  ldc.i4.1
  IL_0072:  ldc.i4.s   9
  IL_0074:  callvirt   ""int C.this[int, int].get""
  IL_0079:  ldc.i4.3
  IL_007a:  add
  IL_007b:  callvirt   ""void C.this[int, int].set""
  IL_0080:  call       ""void System.Console.WriteLine()""
  IL_0085:  ldloc.0
  IL_0086:  stloc.1
  IL_0087:  ldloc.1
  IL_0088:  ldc.i4.1
  IL_0089:  ldc.i4.s   9
  IL_008b:  ldloc.1
  IL_008c:  ldc.i4.1
  IL_008d:  ldc.i4.s   9
  IL_008f:  callvirt   ""int C.this[int, int].get""
  IL_0094:  ldc.i4.3
  IL_0095:  add
  IL_0096:  callvirt   ""void C.this[int, int].set""
  IL_009b:  call       ""void System.Console.WriteLine()""
  IL_00a0:  ldloc.0
  IL_00a1:  stloc.1
  IL_00a2:  ldc.i4.3
  IL_00a3:  newarr     ""int""
  IL_00a8:  dup
  IL_00a9:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_00ae:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_00b3:  stloc.2
  IL_00b4:  ldloc.1
  IL_00b5:  ldloc.2
  IL_00b6:  ldloc.1
  IL_00b7:  ldloc.2
  IL_00b8:  callvirt   ""int C.this[params int[]].get""
  IL_00bd:  ldc.i4.4
  IL_00be:  add
  IL_00bf:  callvirt   ""void C.this[params int[]].set""
  IL_00c4:  call       ""void System.Console.WriteLine()""
  IL_00c9:  ldloc.0
  IL_00ca:  stloc.1
  IL_00cb:  ldc.i4.3
  IL_00cc:  newarr     ""int""
  IL_00d1:  dup
  IL_00d2:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_00d7:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_00dc:  stloc.2
  IL_00dd:  ldloc.1
  IL_00de:  ldloc.2
  IL_00df:  ldloc.1
  IL_00e0:  ldloc.2
  IL_00e1:  callvirt   ""int C.this[params int[]].get""
  IL_00e6:  ldc.i4.4
  IL_00e7:  add
  IL_00e8:  callvirt   ""void C.this[params int[]].set""
  IL_00ed:  call       ""void System.Console.WriteLine()""
  IL_00f2:  ret
}
");
        }
 
        [Fact]
        public void LoweringComplex()
        {
            var text = TypeWithIndexers + @"
class Test
{
    static C NewC() 
    { 
        System.Console.Write(""N,"");
        return new C(); 
    }
 
    static void Main()
    {
        NewC()[y: C.Goo(1), x: NewC()[C.Goo(2)]] = NewC()[x: C.Goo(3), y: NewC()[C.Goo(4)]] += NewC()[C.Goo(5), C.Goo(6), NewC()[C.Goo(7)]]++;
        System.Console.WriteLine();
    }
}
";
            CompileAndVerify(text, expectedOutput: @"
N,1,N,2,20,9,N,3,N,4,40,9,30,-49,N,5,6,N,7,70,9,50,60,-79,50,60,-79,-2,30,-49,16,-29,10,16,
");
        }
 
        [Fact]
        public void BoxingParameterArrayArguments()
        {
            var text = TypeWithIndexers + @"
class Test
{
    int this[params object[] args] { get { return args.Length; } }
 
    static void Main()
    {
        System.Console.WriteLine(new Test()[1, 2]);
    }
}
";
            var verifier = CompileAndVerify(text, expectedOutput: @"2");
 
            verifier.VerifyIL("Test.Main", @"
{
  // Code size       40 (0x28)
  .maxstack  5
  IL_0000:  newobj     ""Test..ctor()""
  IL_0005:  ldc.i4.2
  IL_0006:  newarr     ""object""
  IL_000b:  dup
  IL_000c:  ldc.i4.0
  IL_000d:  ldc.i4.1
  IL_000e:  box        ""int""
  IL_0013:  stelem.ref
  IL_0014:  dup
  IL_0015:  ldc.i4.1
  IL_0016:  ldc.i4.2
  IL_0017:  box        ""int""
  IL_001c:  stelem.ref
  IL_001d:  call       ""int Test.this[params object[]].get""
  IL_0022:  call       ""void System.Console.WriteLine(int)""
  IL_0027:  ret
}
");
        }
 
        [Fact]
        public void IndexerOverrideNotAllAccessors_DefaultParameterValues()
        {
            var text = @"
using System;
 
class Base
{
    public virtual int this[int x, int y = 1]
    {
        get
        {
            Console.WriteLine(""Base.get y: "" + y);
            return y;
        }
        set { Console.WriteLine(""Base.set y: "" + y); }
    }
}
 
class Override : Base
{
    public override int this[int x, int y = 2]
    {
        set { Console.WriteLine(""Override.set y: "" + y); }
    }
}
 
class Program
{
    static void Main()
    {
        Base b = new Base();
        _ = b[0];
        b[0] = 0;
 
        Override o = new Override();
        _ = o[0];
        o[0] = 0;
        o[0] += 0;
    }
}
";
            CompileAndVerify(text, expectedOutput:
@"Base.get y: 1
Base.set y: 1
Base.get y: 1
Override.set y: 2
Base.get y: 1
Override.set y: 1
");
        }
 
        #endregion Lowering
 
        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/75032")]
        public void MissingDefaultMemberAttribute_01()
        {
            var text1 = @"
public interface I1
{
    public I1 this[I1 args] { get; }
}
";
            var comp1 = CreateCompilation(text1);
            comp1.MakeMemberMissing(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor);
 
            comp1.VerifyDiagnostics(
                // (4,15): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor'
                //     public I1 this[I1 args] { get; }
                Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(4, 15)
                );
 
            comp1 = CreateCompilation(text1);
            comp1.MakeTypeMissing(WellKnownType.System_Reflection_DefaultMemberAttribute);
 
            comp1.VerifyDiagnostics(
                // (4,15): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor'
                //     public I1 this[I1 args] { get; }
                Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(4, 15)
                );
 
            comp1 = CreateCompilation(text1);
            comp1.VerifyEmitDiagnostics();
        }
 
        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/75032")]
        public void MissingDefaultMemberAttribute_02()
        {
            var text1 = @"
public interface I1
{
    public I1 this[I1 args] { get; }
}
";
            var comp1 = CreateCompilation(text1);
 
            var text2 = @"
class C : I1
{
    I1 I1.this[I1 args] => throw null;
}
";
            var comp2 = CreateCompilation(text2, references: [comp1.ToMetadataReference()]);
            comp2.MakeMemberMissing(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor);
            comp2.VerifyEmitDiagnostics();
        }
 
        [Fact]
        [WorkItem("https://github.com/dotnet/roslyn/issues/75032")]
        public void MissingDefaultMemberAttribute_03()
        {
            var text1 = @"
using System.Collections.Generic;
 
class Program
{
    static IEnumerable<int> Test()
    {
        return [123];
    }
}
";
            var comp1 = CreateCompilation(text1);
            comp1.MakeMemberMissing(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor);
            CompileAndVerify(comp1).VerifyDiagnostics();
        }
    }
}