|
// 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;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using Basic.Reference.Assemblies;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols
{
public class IndexerTests : CSharpTestBase
{
[ClrOnlyFact]
public void Indexers()
{
var source =
@"using System.Runtime.CompilerServices;
class C
{
[IndexerName(""P"")]
internal string this[string index]
{
get { return null; }
set { }
}
}
interface I
{
object this[int i, params object[] args] { set; }
}
struct S
{
internal object this[string x]
{
get { return null; }
}
}";
Action<ModuleSymbol> validator = module =>
{
var type = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
CheckIndexer(type.Indexers.Single(), true, true, SpecialType.System_String, SpecialType.System_String);
type = module.GlobalNamespace.GetMember<NamedTypeSymbol>("I");
CheckIndexer(type.Indexers.Single(), false, true, SpecialType.System_Object, SpecialType.System_Int32, SpecialType.None);
type = module.GlobalNamespace.GetMember<NamedTypeSymbol>("S");
CheckIndexer(type.Indexers.Single(), true, false, SpecialType.System_Object, SpecialType.System_String);
};
CompileAndVerify(
source: source,
sourceSymbolValidator: validator,
symbolValidator: validator,
options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.Internal));
}
[ClrOnlyFact]
public void InterfaceImplementations()
{
var source =
@"using System.Runtime.CompilerServices;
interface IA
{
object this[string index] { get; set; }
}
interface IB
{
object this[string index] { get; }
}
interface IC
{
[IndexerName(""P"")]
object this[string index] { get; set; }
}
class A : IA, IB, IC
{
object IA.this[string index]
{
get { return null; }
set { }
}
object IB.this[string index]
{
get { return null; }
}
object IC.this[string index]
{
get { return null; }
set { }
}
}
class B : IA, IB, IC
{
public object this[string index]
{
get { return null; }
set { }
}
}
class C : IB, IC
{
[IndexerName(""Q"")]
public object this[string index]
{
get { return null; }
set { }
}
}";
var compilation = CompileAndVerify(source);
compilation.VerifyDiagnostics();
var globalNamespace = (NamespaceSymbol)((CSharpCompilation)compilation.Compilation).GlobalNamespace;
var type = globalNamespace.GetMember<NamedTypeSymbol>("IA");
CheckIndexer(type.Indexers.Single(), true, true, SpecialType.System_Object, SpecialType.System_String);
type = globalNamespace.GetMember<NamedTypeSymbol>("IB");
CheckIndexer(type.Indexers.Single(), true, false, SpecialType.System_Object, SpecialType.System_String);
type = globalNamespace.GetMember<NamedTypeSymbol>("IC");
CheckIndexer(type.Indexers.Single(), true, true, SpecialType.System_Object, SpecialType.System_String);
type = globalNamespace.GetMember<NamedTypeSymbol>("A");
var typeAProperties = type.GetMembers().Where(m => m.Kind == SymbolKind.Property).Cast<PropertySymbol>().ToArray();
Assert.Equal(3, typeAProperties.Length);
CheckIndexer(typeAProperties[0], true, true, SpecialType.System_Object, SpecialType.System_String);
CheckIndexer(typeAProperties[1], true, false, SpecialType.System_Object, SpecialType.System_String);
CheckIndexer(typeAProperties[2], true, true, SpecialType.System_Object, SpecialType.System_String);
var sourceType = globalNamespace.GetMember<SourceNamedTypeSymbol>("B");
CheckIndexer(sourceType.Indexers.Single(), true, true, SpecialType.System_Object, SpecialType.System_String);
var bridgeMethods = sourceType.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods;
Assert.Equal(2, bridgeMethods.Length);
Assert.True(bridgeMethods.Select(GetPairForSynthesizedExplicitImplementation).SetEquals(new[]
{
new KeyValuePair<string, string>("System.Object IC.this[System.String index].get", "System.Object B.this[System.String index].get"),
new KeyValuePair<string, string>("void IC.this[System.String index].set", "void B.this[System.String index].set"),
}));
sourceType = globalNamespace.GetMember<SourceNamedTypeSymbol>("C");
CheckIndexer(sourceType.Indexers.Single(), true, true, SpecialType.System_Object, SpecialType.System_String);
bridgeMethods = sourceType.GetSynthesizedExplicitImplementations(CancellationToken.None).ForwardingMethods;
Assert.Equal(3, bridgeMethods.Length);
Assert.True(bridgeMethods.Select(GetPairForSynthesizedExplicitImplementation).SetEquals(new[]
{
new KeyValuePair<string, string>("System.Object IB.this[System.String index].get", "System.Object C.this[System.String index].get"),
new KeyValuePair<string, string>("System.Object IC.this[System.String index].get", "System.Object C.this[System.String index].get"),
new KeyValuePair<string, string>("void IC.this[System.String index].set", "void C.this[System.String index].set"),
}));
}
private static KeyValuePair<string, string> GetPairForSynthesizedExplicitImplementation(SynthesizedExplicitImplementationForwardingMethod bridge)
{
return new KeyValuePair<string, string>(bridge.ExplicitInterfaceImplementations.Single().ToTestDisplayString(), bridge.ImplementingMethod.ToTestDisplayString());
}
private static void CheckIndexer(PropertySymbol property, bool hasGet, bool hasSet, SpecialType expectedType, params SpecialType[] expectedParameterTypes)
{
Assert.NotNull(property);
Assert.True(property.IsIndexer);
Assert.Equal(property.Type.SpecialType, expectedType);
CheckParameters(property.Parameters, expectedParameterTypes);
var getter = property.GetMethod;
if (hasGet)
{
Assert.NotNull(getter);
Assert.Equal(getter.ReturnType.SpecialType, expectedType);
CheckParameters(getter.Parameters, expectedParameterTypes);
}
else
{
Assert.Null(getter);
}
var setter = property.SetMethod;
if (hasSet)
{
Assert.NotNull(setter);
Assert.True(setter.ReturnsVoid);
CheckParameters(setter.Parameters, expectedParameterTypes.Concat(new[] { expectedType }).ToArray());
}
else
{
Assert.Null(setter);
}
Assert.Equal(property.GetMethod != null, hasGet);
Assert.Equal(property.SetMethod != null, hasSet);
}
private static void CheckParameters(ImmutableArray<ParameterSymbol> parameters, SpecialType[] expectedTypes)
{
Assert.Equal(parameters.Length, expectedTypes.Length);
for (int i = 0; i < expectedTypes.Length; i++)
{
var parameter = parameters[i];
Assert.Equal(parameter.Ordinal, i);
Assert.Equal(parameter.Type.SpecialType, expectedTypes[i]);
}
}
[Fact]
public void OverloadResolution()
{
var source =
@"class C
{
int this[int x, int y]
{
get { return 0; }
}
int F(C c)
{
return this[0] +
c[0, c] +
c[1, 2, 3];
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (9,16): error CS7036: There is no argument given that corresponds to the required parameter 'y' of 'C.this[int, int]'
Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "this[0]").WithArguments("y", "C.this[int, int]").WithLocation(9, 16),
// (10,18): error CS1503: Argument 2: cannot convert from 'C' to 'int'
Diagnostic(ErrorCode.ERR_BadArgType, "c").WithArguments("2", "C", "int").WithLocation(10, 18),
// (11,13): error CS1501: No overload for method 'this' takes 3 arguments
Diagnostic(ErrorCode.ERR_BadArgCount, "c[1, 2, 3]").WithArguments("this", "3").WithLocation(11, 13));
}
[Fact]
public void OverridingHiddenIndexer()
{
var source =
@"
using System.Runtime.CompilerServices;
public class A
{
public virtual int this[int x] { get { return 0; } }
}
public class B : A
{
// Even though the user has specified a name for this indexer that
// doesn't match the name of the base class accessor, we expect
// it to hide A's indexer in subclasses (i.e. C).
[IndexerName(""NotItem"")]
public int this[int x] { get { return 0; } } //NB: not virtual
}
public class C : B
{
public override int this[int x] { get { return 0; } }
}";
var compilation = CreateCompilation(source);
// NOTE: we could eliminate WRN_NewOrOverrideExpected by putting a "new" modifier on B.this[]
compilation.VerifyDiagnostics(
// (15,16): warning CS0114: 'B.this[int]' hides inherited member 'A.this[int]'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "this").WithArguments("B.this[int]", "A.this[int]"),
// (20,25): error CS0506: 'C.this[int]': cannot override inherited member 'B.this[int]' because it is not marked virtual, abstract, or override
Diagnostic(ErrorCode.ERR_CantOverrideNonVirtual, "this").WithArguments("C.this[int]", "B.this[int]"));
var classC = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var indexerC = classC.Indexers.Single();
Assert.Null(indexerC.OverriddenProperty);
Assert.Null(indexerC.GetMethod.OverriddenMethod);
}
[Fact]
public void ImplicitlyImplementingIndexersWithDifferentNames_DifferentInterfaces_Source()
{
var text = @"
using System.Runtime.CompilerServices;
interface I1
{
[IndexerName(""A"")]
int this[int x] { get; }
}
interface I2
{
[IndexerName(""B"")]
int this[int x] { get; }
}
class C : I1, I2
{
public int this[int x] { get { return 0; } }
}
";
var compilation = CreateCompilation(text);
compilation.VerifyDiagnostics();
var interface1 = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("I1");
var interface1Indexer = interface1.Indexers.Single();
var interface2 = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("I2");
var interface2Indexer = interface2.Indexers.Single();
var @class = compilation.GlobalNamespace.GetMember<SourceNamedTypeSymbol>("C");
var classIndexer = @class.Indexers.Single();
// All of the indexers have the same Name
Assert.Equal(WellKnownMemberNames.Indexer, classIndexer.Name);
Assert.Equal(WellKnownMemberNames.Indexer, interface1Indexer.Name);
Assert.Equal(WellKnownMemberNames.Indexer, interface2Indexer.Name);
// All of the indexers have different MetadataNames
Assert.NotEqual(interface1Indexer.MetadataName, interface2Indexer.MetadataName);
Assert.NotEqual(interface1Indexer.MetadataName, classIndexer.MetadataName);
Assert.NotEqual(interface2Indexer.MetadataName, classIndexer.MetadataName);
// classIndexer implements both
Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interface1Indexer));
Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interface2Indexer));
var synthesizedExplicitImplementations = @class.GetSynthesizedExplicitImplementations(default(CancellationToken)).ForwardingMethods;
Assert.Equal(2, synthesizedExplicitImplementations.Length);
Assert.Equal(classIndexer.GetMethod, synthesizedExplicitImplementations[0].ImplementingMethod);
Assert.Equal(classIndexer.GetMethod, synthesizedExplicitImplementations[1].ImplementingMethod);
var interface1Getter = interface1Indexer.GetMethod;
var interface2Getter = interface2Indexer.GetMethod;
var interface1GetterImpl = synthesizedExplicitImplementations[0].ExplicitInterfaceImplementations.Single();
var interface2GetterImpl = synthesizedExplicitImplementations[1].ExplicitInterfaceImplementations.Single();
Assert.True(interface1Getter == interface1GetterImpl ^ interface1Getter == interface2GetterImpl);
Assert.True(interface2Getter == interface1GetterImpl ^ interface2Getter == interface2GetterImpl);
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void ImplicitlyImplementingIndexersWithDifferentNames_DifferentInterfaces_Metadata()
{
var il = @"
.class interface public abstract auto ansi I1
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('A')}
.method public hidebysig newslot specialname abstract virtual
instance int32 get_A(int32 x) cil managed
{
} // end of method I1::get_A
.property instance int32 A(int32)
{
.get instance int32 I1::get_A(int32)
} // end of property I1::A
} // end of class I1
.class interface public abstract auto ansi I2
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('B')}
.method public hidebysig newslot specialname abstract virtual
instance int32 get_B(int32 x) cil managed
{
} // end of method I2::get_B
.property instance int32 B(int32)
{
.get instance int32 I2::get_B(int32)
} // end of property I2::B
} // end of class I2
";
var csharp = @"
class C : I1, I2
{
public int this[int x] { get { return 0; } }
}
";
CompileWithCustomILSource(csharp, il, compilation =>
{
var interface1 = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("I1");
var interface1Indexer = interface1.Indexers.Single();
var interface2 = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("I2");
var interface2Indexer = interface2.Indexers.Single();
var @class = compilation.GlobalNamespace.GetMember<SourceNamedTypeSymbol>("C");
var classIndexer = @class.Indexers.Single();
// All of the indexers have the same Name
Assert.Equal(WellKnownMemberNames.Indexer, classIndexer.Name);
Assert.Equal(WellKnownMemberNames.Indexer, interface1Indexer.Name);
Assert.Equal(WellKnownMemberNames.Indexer, interface2Indexer.Name);
// All of the indexers have different MetadataNames
Assert.NotEqual(interface1Indexer.MetadataName, interface2Indexer.MetadataName);
Assert.NotEqual(interface1Indexer.MetadataName, classIndexer.MetadataName);
Assert.NotEqual(interface2Indexer.MetadataName, classIndexer.MetadataName);
// classIndexer implements both
Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interface1Indexer));
Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interface2Indexer));
var synthesizedExplicitImplementations = @class.GetSynthesizedExplicitImplementations(default(CancellationToken)).ForwardingMethods;
Assert.Equal(2, synthesizedExplicitImplementations.Length);
Assert.Equal(classIndexer.GetMethod, synthesizedExplicitImplementations[0].ImplementingMethod);
Assert.Equal(classIndexer.GetMethod, synthesizedExplicitImplementations[1].ImplementingMethod);
var interface1Getter = interface1Indexer.GetMethod;
var interface2Getter = interface2Indexer.GetMethod;
var interface1GetterImpl = synthesizedExplicitImplementations[0].ExplicitInterfaceImplementations.Single();
var interface2GetterImpl = synthesizedExplicitImplementations[1].ExplicitInterfaceImplementations.Single();
Assert.True(interface1Getter == interface1GetterImpl ^ interface1Getter == interface2GetterImpl);
Assert.True(interface2Getter == interface1GetterImpl ^ interface2Getter == interface2GetterImpl);
});
}
/// <summary>
/// Metadata type has two indexers with the same signature but different names.
/// Both are implicitly implemented by a single source indexer.
/// </summary>
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void ImplicitlyImplementingIndexersWithDifferentNames_SameInterface()
{
var il = @"
.class interface public abstract auto ansi I1
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('getter')}
.method public hidebysig newslot specialname abstract virtual
instance int32 getter(int32 x) cil managed
{
} // end of method I1::getter
.property instance int32 A(int32)
{
.get instance int32 I1::getter(int32)
} // end of property I1::A
.property instance int32 B(int32)
{
.get instance int32 I1::getter(int32)
} // end of property I1::B
} // end of class I1
";
var csharp = @"
class C : I1
{
public int this[int x] { get { return 0; } }
}
";
CompileWithCustomILSource(csharp, il, compilation =>
{
var @interface = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("I1");
var interfaceIndexers = @interface.Indexers;
Assert.Equal(2, interfaceIndexers.Length);
Assert.Equal(interfaceIndexers[0].ToTestDisplayString(), interfaceIndexers[1].ToTestDisplayString());
var @class = compilation.GlobalNamespace.GetMember<SourceNamedTypeSymbol>("C");
var classIndexer = @class.Indexers.Single();
// classIndexer implements both
Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interfaceIndexers[0]));
Assert.Equal(classIndexer, @class.FindImplementationForInterfaceMember(interfaceIndexers[1]));
var synthesizedExplicitImplementation = @class.GetSynthesizedExplicitImplementations(default(CancellationToken)).ForwardingMethods.Single();
Assert.Equal(classIndexer.GetMethod, synthesizedExplicitImplementation.ImplementingMethod);
Assert.Equal(interfaceIndexers[0].GetMethod, synthesizedExplicitImplementation.ExplicitInterfaceImplementations.Single());
Assert.Equal(interfaceIndexers[1].GetMethod, synthesizedExplicitImplementation.ExplicitInterfaceImplementations.Single());
});
}
/// <summary>
/// Metadata type has two indexers with the same signature but different names.
/// Both are explicitly implemented by a single source indexer, resulting in an
/// ambiguity error.
/// </summary>
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void AmbiguousExplicitIndexerImplementation()
{
// NOTE: could be done in C# using IndexerNameAttribute
var il = @"
.class interface public abstract auto ansi I1
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('get_Item')}
.method public hidebysig newslot specialname abstract virtual
instance int32 get_Item(int32 x) cil managed
{
} // end of method I1::get_Item
.property instance int32 A(int32)
{
.get instance int32 I1::get_Item(int32)
} // end of property I1::A
.property instance int32 B(int32)
{
.get instance int32 I1::get_Item(int32)
} // end of property I1::B
} // end of class I1
";
var csharp1 = @"
class C : I1
{
int I1.this[int x] { get { return 0; } }
}
";
var compilation = CreateCompilationWithILAndMscorlib40(csharp1, il).VerifyDiagnostics(
// (4,12): warning CS0473: Explicit interface implementation 'C.I1.this[int]' matches more than one interface member. Which interface member is actually chosen is implementation-dependent. Consider using a non-explicit implementation instead.
Diagnostic(ErrorCode.WRN_ExplicitImplCollision, "this").WithArguments("C.I1.this[int]"),
// (2,7): error CS0535: 'C' does not implement interface member 'I1.this[int]'
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C", "I1.this[int]"));
var @interface = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("I1");
var interfaceIndexers = @interface.Indexers;
Assert.Equal(2, interfaceIndexers.Length);
Assert.Equal(interfaceIndexers[0].ToTestDisplayString(), interfaceIndexers[1].ToTestDisplayString());
var @class = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var classIndexer = @class.GetProperty("I1.this[]");
// One is implemented, the other is not (unspecified which)
var indexer0Impl = @class.FindImplementationForInterfaceMember(interfaceIndexers[0]);
var indexer1Impl = @class.FindImplementationForInterfaceMember(interfaceIndexers[1]);
Assert.True(indexer0Impl == classIndexer ^ indexer1Impl == classIndexer);
Assert.True(indexer0Impl == null ^ indexer1Impl == null);
var csharp2 = @"
class C : I1
{
public int this[int x] { get { return 0; } }
}
";
compilation = CreateCompilationWithILAndMscorlib40(csharp2, il).VerifyDiagnostics();
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void HidingIndexerWithDifferentName()
{
// NOTE: could be done in C# using IndexerNameAttribute
var il = @"
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('A')}
.method public hidebysig specialname instance int32
get_A(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 A(int32)
{
.get instance int32 Base::get_A(int32)
} // end of property Base::A
} // end of class Base
";
var csharp = @"
class Derived : Base
{
public int this[int x] { get { return 0; } }
}
";
var compilation = CreateCompilationWithILAndMscorlib40(csharp, il);
compilation.VerifyDiagnostics(
// (4,16): warning CS0108: 'Derived.this[int]' hides inherited member 'Base.this[int]'. Use the new keyword if hiding was intended.
Diagnostic(ErrorCode.WRN_NewRequired, "this").WithArguments("Derived.this[int]", "Base.this[int]"));
var baseClass = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Base");
var baseIndexer = baseClass.Indexers.Single();
var derivedClass = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Derived");
var derivedIndexer = derivedClass.Indexers.Single();
// The indexers have the same Name
Assert.Equal(WellKnownMemberNames.Indexer, derivedIndexer.Name);
Assert.Equal(WellKnownMemberNames.Indexer, baseIndexer.Name);
// The indexers have different MetadataNames
Assert.NotEqual(baseIndexer.MetadataName, derivedIndexer.MetadataName);
Assert.Equal(baseIndexer, derivedIndexer.OverriddenOrHiddenMembers.HiddenMembers.Single());
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void OverridingIndexerWithDifferentName()
{
// NOTE: could be done in C# using IndexerNameAttribute
var il = @"
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('A')}
.method public hidebysig newslot specialname virtual
instance int32 get_A(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 A(int32)
{
.get instance int32 Base::get_A(int32)
} // end of property Base::A
} // end of class Base
";
var csharp = @"
class Derived : Base
{
public override int this[int x] { get { return 0; } }
}
";
CompileWithCustomILSource(csharp, il, compilation =>
{
var baseClass = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Base");
var baseIndexer = baseClass.Indexers.Single();
var derivedClass = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Derived");
var derivedIndexer = derivedClass.Indexers.Single();
// Rhe indexers have the same Name
Assert.Equal(WellKnownMemberNames.Indexer, derivedIndexer.Name);
Assert.Equal(WellKnownMemberNames.Indexer, baseIndexer.Name);
// The indexers have different MetadataNames
Assert.NotEqual(baseIndexer.MetadataName, derivedIndexer.MetadataName);
Assert.Equal(baseIndexer, derivedIndexer.OverriddenProperty);
});
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void HidingMultipleIndexers()
{
// NOTE: could be done in C# using IndexerNameAttribute
var il = @"
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('getter')}
.method public hidebysig specialname instance int32
getter(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 A(int32)
{
.get instance int32 Base::getter(int32)
} // end of property Base::A
.property instance int32 B(int32)
{
.get instance int32 Base::getter(int32)
} // end of property Base::B
} // end of class Base
";
var csharp = @"
class Derived : Base
{
public int this[int x] { get { return 0; } }
}
";
var compilation = CreateCompilationWithILAndMscorlib40(csharp, il);
// As in dev10, we report only the first hidden member.
compilation.VerifyDiagnostics(
// (4,16): warning CS0108: 'Derived.this[int]' hides inherited member 'Base.this[int]'. Use the new keyword if hiding was intended.
Diagnostic(ErrorCode.WRN_NewRequired, "this").WithArguments("Derived.this[int]", "Base.this[int]"));
var baseClass = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Base");
var baseIndexers = baseClass.Indexers;
var derivedClass = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Derived");
var derivedIndexer = derivedClass.Indexers.Single();
// The indexers have the same Name
Assert.Equal(WellKnownMemberNames.Indexer, derivedIndexer.Name);
Assert.Equal(WellKnownMemberNames.Indexer, baseIndexers[0].Name);
Assert.Equal(WellKnownMemberNames.Indexer, baseIndexers[1].Name);
// The indexers have different MetadataNames
Assert.NotEqual(baseIndexers[0].MetadataName, baseIndexers[1].MetadataName);
Assert.NotEqual(baseIndexers[0].MetadataName, derivedIndexer.MetadataName);
Assert.NotEqual(baseIndexers[1].MetadataName, derivedIndexer.MetadataName);
// classIndexer implements both
var hiddenMembers = derivedIndexer.OverriddenOrHiddenMembers.HiddenMembers;
Assert.Equal(2, hiddenMembers.Length);
Assert.Contains(baseIndexers[0], hiddenMembers);
Assert.Contains(baseIndexers[1], hiddenMembers);
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void OverridingMultipleIndexers()
{
// NOTE: could be done in C# using IndexerNameAttribute
var il = @"
.class public auto ansi beforefieldinit Base
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('getter')}
.method public hidebysig newslot specialname virtual
instance int32 getter(int32 x) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 A(int32)
{
.get instance int32 Base::getter(int32)
} // end of property Base::A
.property instance int32 B(int32)
{
.get instance int32 Base::getter(int32)
} // end of property Base::B
} // end of class Base
";
var csharp = @"
class Derived : Base
{
public override int this[int x] { get { return 0; } }
}
";
var compilation = CreateCompilationWithILAndMscorlib40(csharp, il).VerifyDiagnostics(
// (4,25): error CS0462: The inherited members 'Base.this[int]' and 'Base.this[int]' have the same signature in type 'Derived', so they cannot be overridden
Diagnostic(ErrorCode.ERR_AmbigOverride, "this").WithArguments("Base.this[int]", "Base.this[int]", "Derived"));
var baseClass = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Base");
var baseIndexers = baseClass.Indexers;
var derivedClass = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Derived");
var derivedIndexer = derivedClass.Indexers.Single();
// The indexers have the same Name
Assert.Equal(WellKnownMemberNames.Indexer, derivedIndexer.Name);
Assert.Equal(WellKnownMemberNames.Indexer, baseIndexers[0].Name);
Assert.Equal(WellKnownMemberNames.Indexer, baseIndexers[1].Name);
// The indexers have different MetadataNames
Assert.NotEqual(baseIndexers[0].MetadataName, baseIndexers[1].MetadataName);
Assert.NotEqual(baseIndexers[0].MetadataName, derivedIndexer.MetadataName);
Assert.NotEqual(baseIndexers[1].MetadataName, derivedIndexer.MetadataName);
// classIndexer implements both
var overriddenMembers = derivedIndexer.OverriddenOrHiddenMembers.OverriddenMembers;
Assert.Equal(2, overriddenMembers.Length);
Assert.Contains(baseIndexers[0], overriddenMembers);
Assert.Contains(baseIndexers[1], overriddenMembers);
}
[Fact]
public void IndexerAccessErrors()
{
var source =
@"class C
{
public int this[int x, long y] { get { return x; } set { } }
void M(C c)
{
c[0] = c[0, 0, 0]; //wrong number of arguments
c[true, 1] = c[y: 1, x: long.MaxValue]; //wrong argument types
c[1, x: 1] = c[x: 1, 2]; //bad mix of named and positional
this[q: 1, r: 2] = base[0]; //bad parameter names / no indexer
}
}";
CreateCompilation(source, parseOptions: TestOptions.Regular7_1).VerifyDiagnostics(
// (7,9): error CS7036: There is no argument given that corresponds to the required parameter 'y' of 'C.this[int, long]'
// c[0] = c[0, 0, 0]; //wrong number of arguments
Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "c[0]").WithArguments("y", "C.this[int, long]").WithLocation(7, 9),
// (7,16): error CS1501: No overload for method 'this' takes 3 arguments
// c[0] = c[0, 0, 0]; //wrong number of arguments
Diagnostic(ErrorCode.ERR_BadArgCount, "c[0, 0, 0]").WithArguments("this", "3").WithLocation(7, 16),
// (8,11): error CS1503: Argument 1: cannot convert from 'bool' to 'int'
// c[true, 1] = c[y: 1, x: long.MaxValue]; //wrong argument types
Diagnostic(ErrorCode.ERR_BadArgType, "true").WithArguments("1", "bool", "int").WithLocation(8, 11),
// (8,33): error CS1503: Argument 2: cannot convert from 'long' to 'int'
// c[true, 1] = c[y: 1, x: long.MaxValue]; //wrong argument types
Diagnostic(ErrorCode.ERR_BadArgType, "long.MaxValue").WithArguments("2", "long", "int").WithLocation(8, 33),
// (9,14): error CS1744: Named argument 'x' specifies a parameter for which a positional argument has already been given
// c[1, x: 1] = c[x: 1, 2]; //bad mix of named and positional
Diagnostic(ErrorCode.ERR_NamedArgumentUsedInPositional, "x").WithArguments("x").WithLocation(9, 14),
// (9,30): error CS1738: Named argument specifications must appear after all fixed arguments have been specified. Please use language version 7.2 or greater to allow non-trailing named arguments.
// c[1, x: 1] = c[x: 1, 2]; //bad mix of named and positional
Diagnostic(ErrorCode.ERR_NamedArgumentSpecificationBeforeFixedArgument, "2").WithArguments("7.2").WithLocation(9, 30),
// (10,14): error CS1739: The best overload for 'this' does not have a parameter named 'q'
// this[q: 1, r: 2] = base[0]; //bad parameter names / no indexer
Diagnostic(ErrorCode.ERR_BadNamedArgument, "q").WithArguments("this", "q").WithLocation(10, 14),
// (10,28): error CS0021: Cannot apply indexing with [] to an expression of type 'object'
// this[q: 1, r: 2] = base[0]; //bad parameter names / no indexer
Diagnostic(ErrorCode.ERR_BadIndexLHS, "base[0]").WithArguments("object").WithLocation(10, 28)
);
}
[Fact]
public void OverloadResolutionOnIndexersNotAccessors()
{
var source =
@"class C
{
public int this[int x] { set { } }
public int this[int x, double d = 1] { get { return x; } set { } }
void M(C c)
{
int x = c[0]; //pick the first overload, even though it has no getter and the second would work
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (8,17): error CS0154: The property or indexer 'C.this[int]' cannot be used in this context because it lacks the get accessor
Diagnostic(ErrorCode.ERR_PropertyLacksGet, "c[0]").WithArguments("C.this[int]"));
}
[Fact]
public void UseExplicitInterfaceImplementationAccessor()
{
var source =
@"interface I
{
int this[int x] { get; }
}
class C : I
{
int I.this[int x] { get { return x; } }
void M(C c)
{
int x = c[0]; // no indexer found
int y = ((I)c)[0];
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (13,17): error CS0021: Cannot apply indexing with [] to an expression of type 'C'
Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[0]").WithArguments("C"));
}
[Fact]
public void UsePropertyAndAccessorsDirectly()
{
var source =
@"class C
{
int this[int x] { get { return x; } set { } }
void M(C c)
{
int x = c.Item[1]; //CS1061 - no such member
int y = c.get_Item(1); //CS0571 - use the indexer
c.set_Item(y); //CS0571 - use the indexer
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (7,19): error CS1061: 'C' does not contain a definition for 'Item' and no extension method 'Item' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?)
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Item").WithArguments("C", "Item"),
// (8,19): error CS0571: 'C.this[int].get': cannot explicitly call operator or accessor
Diagnostic(ErrorCode.ERR_CantCallSpecialMethod, "get_Item").WithArguments("C.this[int].get"),
// (9,11): error CS0571: 'C.this[int].set': cannot explicitly call operator or accessor
Diagnostic(ErrorCode.ERR_CantCallSpecialMethod, "set_Item").WithArguments("C.this[int].set"));
}
[Fact]
public void NestedIndexerAccesses()
{
var source =
@"class C
{
C this[int x] { get { return this; } set { } }
int[] this[char x] { get { return null; } set { } }
void M(C c)
{
int x = c[0][1][2][3]['a'][1]; //fine
}
}";
CreateCompilation(source).VerifyDiagnostics();
}
[Fact]
public void NamedParameters()
{
var source =
@"class C
{
int this[int x, string y, char z] { get { return x; } }
void M(C c)
{
int x;
x = c[x: 0, y: ""hello"", z:'a'];
x = c[0, y: ""hello"", z:'a'];
x = c[0, ""hello"", z:'a'];
x = c[0, ""hello"", 'a'];
x = c[z: 'a', x: 0, y: ""hello""]; //all reordered
x = c[0, z:'a', y: ""hello""]; //some reordered
}
}";
CreateCompilation(source).VerifyDiagnostics();
}
[Fact]
public void OptionalParameters()
{
var source =
@"class C
{
int this[int x = 1, string y = ""goodbye"", char z = 'b'] { get { return x; } }
void M(C c)
{
int x;
x = this[]; //CS0443 - can't omit all
x = c[x: 0];
x = c[y: ""hello""];
x = c[z:'a'];
x = c[x: 0, y: ""hello""];
x = c[x: 0, z:'a'];
x = c[y: ""hello"", z:'a'];
x = c[x: 0, y: ""hello"", z:'a'];
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (8,18): error CS0443: Syntax error; value expected
Diagnostic(ErrorCode.ERR_ValueExpected, "]"));
}
[Fact]
public void ParameterArray()
{
var source =
@"class C
{
int this[params int[] args] { get { return 0; } }
int this[char c, params char[] args] { get { return 0; } }
void M(C c)
{
int x;
x = this[]; //CS0443 - can't omit all
x = c[0];
x = c[0, 1];
x = c[0, 1, 2];
x = c[new int[3]];
x = c[args: new int[3]];
x = c['a'];
x = c['a', 'b'];
x = c['a', 'b', 'c'];
x = c['a', new char[3]];
x = c['a', args: new char[3]];
x = c[args: new char[3], c: 'a'];
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (9,18): error CS0443: Syntax error; value expected
Diagnostic(ErrorCode.ERR_ValueExpected, "]"));
}
[Fact]
public void StaticIndexer()
{
var source =
@"class C
{
// Illegal, but we shouldn't blow up
public static int this[char c] { get { return 0; } } //CS0106 - illegal modifier
public static void Main()
{
int x = C['a']; //CS0119 - can't use a type here
int y = new C()['a']; //we don't even check for this kind of error because it's always cascading
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (4,23): error CS0106: The modifier 'static' is not valid for this item
Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(4, 23),
// (8,17): error CS0119: 'C' is a 'type', which is not valid in the given context
Diagnostic(ErrorCode.ERR_BadSKunknown, "C").WithArguments("C", "type").WithLocation(8, 17));
}
[Fact]
public void OverridingAndHidingWithExplicitIndexerName()
{
var source =
@"using System;
using System.Runtime.CompilerServices;
public class A
{
public virtual int this[int x]
{
get
{
Console.WriteLine(""A"");
return 0;
}
}
}
public class B : A
{
[IndexerName(""NotItem"")]
public int this[int x]
{
get
{
Console.WriteLine(""B"");
return 0;
}
}
}
public class C : B
{
public override int this[int x]
{
get
{
Console.WriteLine(""C"");
return 0;
}
}
}";
// Doesn't matter that B's indexer has an explicit name - the symbols are all called "this[]".
CreateCompilation(source).VerifyDiagnostics(
// (19,16): warning CS0114: 'B.this[int]' hides inherited member 'A.this[int]'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "this").WithArguments("B.this[int]", "A.this[int]"),
// (31,25): error CS0506: 'C.this[int]': cannot override inherited member 'B.this[int]' because it is not marked virtual, abstract, or override
Diagnostic(ErrorCode.ERR_CantOverrideNonVirtual, "this").WithArguments("C.this[int]", "B.this[int]"));
}
[ClrOnlyFact]
public void CanBeReferencedByName()
{
var source = @"
interface I
{
event System.Action E;
int P { get; set; }
int this[int x] { set; }
}
class C : I
{
event System.Action I.E { add { } remove { } }
public event System.Action E;
int I.P { get; set; }
public int P { get; set; }
int I.this[int x] { set { } }
public int this[int x] { set { } }
}
";
Func<bool, Action<ModuleSymbol>> validator = isFromSource => module =>
{
var globalNamespace = module.GlobalNamespace;
var compilation = module.DeclaringCompilation;
Assert.Equal(isFromSource, compilation != null);
//// Source interface
var @interface = globalNamespace.GetMember<NamedTypeSymbol>("I");
if (isFromSource)
{
Assert.True(@interface.IsFromCompilation(compilation));
}
var interfaceEvent = @interface.GetMember<EventSymbol>("E");
var interfaceProperty = @interface.GetMember<PropertySymbol>("P");
var interfaceIndexer = @interface.Indexers.Single();
Assert.True(interfaceEvent.CanBeReferencedByName);
Assert.True(interfaceProperty.CanBeReferencedByName);
Assert.False(interfaceIndexer.CanBeReferencedByName);
//// Source class
var @class = globalNamespace.GetMember<NamedTypeSymbol>("C");
if (isFromSource)
{
Assert.True(@class.IsFromCompilation(compilation));
}
var classEventImpl = @class.GetMembers().Where(m => m.GetExplicitInterfaceImplementations().Contains(interfaceEvent)).Single();
var classPropertyImpl = @class.GetMembers().Where(m => m.GetExplicitInterfaceImplementations().Contains(interfaceProperty)).Single();
var classIndexerImpl = @class.GetMembers().Where(m => m.GetExplicitInterfaceImplementations().Contains(interfaceIndexer)).Single();
Assert.False(classEventImpl.CanBeReferencedByName);
Assert.False(classPropertyImpl.CanBeReferencedByName);
Assert.False(classIndexerImpl.CanBeReferencedByName);
var classEvent = @class.GetMember<EventSymbol>("E");
var classProperty = @class.GetMember<PropertySymbol>("P");
var classIndexer = @class.Indexers.Single();
Assert.True(classEvent.CanBeReferencedByName);
Assert.True(classProperty.CanBeReferencedByName);
Assert.False(classIndexer.CanBeReferencedByName);
};
CompileAndVerify(source, sourceSymbolValidator: validator(true), symbolValidator: validator(false));
}
[Fact]
public void RegressFinalValidationAssert()
{
var source =
@"class C
{
int this[int x] { get { return x; } }
void M()
{
System.Console.WriteLine(this[0]);
}
}";
CreateCompilation(source).VerifyDiagnostics();
}
/// <summary>
/// The Name and IsIndexer bits of explicitly implemented interface indexers do not roundtrip.
/// This is unfortunate, but less so that having something declared with an IndexerDeclarationSyntax
/// return false for IsIndexer.
/// </summary>
[ClrOnlyFact]
public void ExplicitInterfaceImplementationIndexers()
{
var text = @"
public interface I
{
int this[int x] { set; }
}
public class C : I
{
int I.this[int x] { set { } }
}
";
Action<ModuleSymbol> sourceValidator = module =>
{
var globalNamespace = module.GlobalNamespace;
var classC = globalNamespace.GetMember<NamedTypeSymbol>("C");
Assert.Equal(0, classC.Indexers.Length); //excludes explicit implementations
var classCIndexer = classC.GetMembers().Where(s => s.Kind == SymbolKind.Property).Single();
Assert.Equal("I.this[]", classCIndexer.Name); //interface name + WellKnownMemberNames.Indexer
Assert.True(classCIndexer.IsIndexer()); //since declared with IndexerDeclarationSyntax
};
Action<ModuleSymbol> metadataValidator = module =>
{
var globalNamespace = module.GlobalNamespace;
var classC = globalNamespace.GetMember<NamedTypeSymbol>("C");
Assert.Equal(0, classC.Indexers.Length); //excludes explicit implementations
var classCIndexer = classC.GetMembers().Where(s => s.Kind == SymbolKind.Property).Single();
Assert.Equal("I.Item", classCIndexer.Name); //name does not reflect WellKnownMemberNames.Indexer
Assert.False(classCIndexer.IsIndexer()); //not the default member of C
};
CompileAndVerify(text, sourceSymbolValidator: sourceValidator, symbolValidator: metadataValidator);
}
[Fact]
public void NoAutoIndexers()
{
var source =
@"class B
{
public virtual int this[int x] { get; set; }
}";
CreateCompilation(source).VerifyDiagnostics(
// (3,38): error CS0501: 'B.this[int].get' must declare a body because it is not marked abstract, extern, or partial
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "get").WithArguments("B.this[int].get"),
// (3,43): error CS0501: 'B.this[int].set' must declare a body because it is not marked abstract, extern, or partial
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "set").WithArguments("B.this[int].set"));
}
[Fact]
public void BaseIndexerAccess()
{
var source =
@"public class Base
{
public int this[int x] { get { return x; } }
}
public class Derived : Base
{
public new int this[int x] { get { return x; } }
void Method()
{
int x = base[1];
}
}";
var tree = Parse(source);
var comp = CreateCompilation(tree);
comp.VerifyDiagnostics();
var indexerAccessSyntax = GetElementAccessExpressions(tree.GetCompilationUnitRoot()).Single();
var baseClass = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("Base");
var baseIndexer = baseClass.Indexers.Single();
// Confirm that the base indexer is used (even though the derived indexer signature matches).
var model = comp.GetSemanticModel(tree);
var symbolInfo = model.GetSymbolInfo(indexerAccessSyntax);
Assert.Equal(baseIndexer.GetPublicSymbol(), symbolInfo.Symbol);
}
/// <summary>
/// Indexers cannot have ref params in source, but they can in metadata.
/// </summary>
[Fact]
public void IndexerWithRefParameter_Access()
{
var source = @"
class Test
{
static void Main()
{
RefIndexer r = new RefIndexer();
int x = 1;
x = r[ref x];
r[ref x] = 1;
r[ref x]++;
r[ref x] += 2;
}
}
";
var compilation = CreateCompilation(source, new[] { TestReferences.SymbolsTests.Indexers });
compilation.VerifyDiagnostics(
// (8,13): error CS1545: Property, indexer, or event 'RefIndexer.this[ref int]' is not supported by the language; try directly calling accessor methods 'RefIndexer.get_Item(ref int)' or 'RefIndexer.set_Item(ref int, int)'
Diagnostic(ErrorCode.ERR_BindToBogusProp2, "r[ref x]").WithArguments("RefIndexer.this[ref int]", "RefIndexer.get_Item(ref int)", "RefIndexer.set_Item(ref int, int)"),
// (9,9): error CS1545: Property, indexer, or event 'RefIndexer.this[ref int]' is not supported by the language; try directly calling accessor methods 'RefIndexer.get_Item(ref int)' or 'RefIndexer.set_Item(ref int, int)'
Diagnostic(ErrorCode.ERR_BindToBogusProp2, "r[ref x]").WithArguments("RefIndexer.this[ref int]", "RefIndexer.get_Item(ref int)", "RefIndexer.set_Item(ref int, int)"),
// (10,9): error CS1545: Property, indexer, or event 'RefIndexer.this[ref int]' is not supported by the language; try directly calling accessor methods 'RefIndexer.get_Item(ref int)' or 'RefIndexer.set_Item(ref int, int)'
Diagnostic(ErrorCode.ERR_BindToBogusProp2, "r[ref x]").WithArguments("RefIndexer.this[ref int]", "RefIndexer.get_Item(ref int)", "RefIndexer.set_Item(ref int, int)"),
// (11,9): error CS1545: Property, indexer, or event 'RefIndexer.this[ref int]' is not supported by the language; try directly calling accessor methods 'RefIndexer.get_Item(ref int)' or 'RefIndexer.set_Item(ref int, int)'
Diagnostic(ErrorCode.ERR_BindToBogusProp2, "r[ref x]").WithArguments("RefIndexer.this[ref int]", "RefIndexer.get_Item(ref int)", "RefIndexer.set_Item(ref int, int)"));
}
/// <summary>
/// Indexers cannot have ref params in source, but they can in metadata.
/// </summary>
[Fact]
public void IndexerWithRefParameter_CallAccessor()
{
var source = @"
class Test
{
static void Main()
{
RefIndexer r = new RefIndexer();
int x = 1;
x = r.get_Item(ref x);
r.set_Item(ref x, 1);
}
}
";
var compilation = CreateCompilation(source, new[] { TestReferences.SymbolsTests.Indexers });
compilation.VerifyDiagnostics();
}
/// <summary>
/// Indexers cannot have ref params in source, but they can in metadata.
/// </summary>
[Fact]
public void IndexerWithRefParameter_Override()
{
var source = @"
class Test : RefIndexer
{
public override int this[int x] { get { return 0; } set { } }
}
";
var compilation = CreateCompilation(source,
new MetadataReference[] { TestReferences.SymbolsTests.Indexers });
compilation.VerifyDiagnostics(
// (4,25): error CS0115: 'Test.this[int]': no suitable method found to override
Diagnostic(ErrorCode.ERR_OverrideNotExpected, "this").WithArguments("Test.this[int]"));
}
/// <summary>
/// Indexers cannot have ref params in source, but they can in metadata.
/// </summary>
[Fact]
public void IndexerWithRefParameter_ImplicitlyImplement()
{
var source = @"
class Test : IRefIndexer
{
public int this[int x] { get { return 0; } set { } }
}
";
var compilation = CreateCompilation(source, new[] { TestReferences.SymbolsTests.Indexers });
// Normally, we wouldn't see errors for the accessors, but here we do because the indexer is bogus.
compilation.VerifyDiagnostics(
// (2,7): error CS0535: 'Test' does not implement interface member 'IRefIndexer.get_Item(ref int)'
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "IRefIndexer").WithArguments("Test", "IRefIndexer.get_Item(ref int)"),
// (2,7): error CS0535: 'Test' does not implement interface member 'IRefIndexer.set_Item(ref int, int)'
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "IRefIndexer").WithArguments("Test", "IRefIndexer.set_Item(ref int, int)"));
}
/// <summary>
/// Indexers cannot have ref params in source, but they can in metadata.
/// </summary>
[Fact]
public void IndexerWithRefParameter_ExplicitlyImplement()
{
var source = @"
class Test : IRefIndexer
{
int IRefIndexer.this[int x] { get { return 0; } set { } }
}
";
var compilation = CreateCompilation(source,
new MetadataReference[] { TestReferences.SymbolsTests.Indexers });
// Normally, we wouldn't see errors for the accessors, but here we do because the indexer is bogus.
compilation.VerifyDiagnostics(
// (4,21): error CS0539: 'Test.this[int]' in explicit interface declaration is not a member of interface
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "this").WithArguments("Test.this[int]"),
// (2,7): error CS0535: 'Test' does not implement interface member 'IRefIndexer.get_Item(ref int)'
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "IRefIndexer").WithArguments("Test", "IRefIndexer.get_Item(ref int)"),
// (2,7): error CS0535: 'Test' does not implement interface member 'IRefIndexer.set_Item(ref int, int)'
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "IRefIndexer").WithArguments("Test", "IRefIndexer.set_Item(ref int, int)"));
}
[Fact]
public void IndexerNameAttribute()
{
var source = @"
using System.Runtime.CompilerServices;
class B
{
[IndexerName(""A"")]
public virtual int this[int x] { get { return 0; } set { } }
}
";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var indexer = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("B").Indexers.Single();
Assert.Equal(WellKnownMemberNames.Indexer, indexer.Name);
Assert.Equal("A", indexer.MetadataName);
Assert.Equal("get_A", indexer.GetMethod.Name);
Assert.Equal("get_A", indexer.GetMethod.MetadataName);
Assert.Equal("set_A", indexer.SetMethod.Name);
Assert.Equal("set_A", indexer.SetMethod.MetadataName);
}
[WorkItem(528830, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528830")]
[Fact(Skip = "528830")]
public void EscapedIdentifierInIndexerNameAttribute()
{
var source = @"
using System.Runtime.CompilerServices;
interface I
{
[IndexerName(""@indexer"")]
int this[int x] { get; set; }
}
";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var indexer = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("I").Indexers.Single();
Assert.Equal("@indexer", indexer.MetadataName);
Assert.Equal("get_@indexer", indexer.GetMethod.MetadataName);
Assert.Equal("set_@indexer", indexer.SetMethod.MetadataName);
}
[Fact]
public void NameNotCopiedOnOverride1()
{
var source = @"
using System.Runtime.CompilerServices;
class B
{
[IndexerName(""A"")]
public virtual int this[int x] { get { return 0; } set { } }
}
class D : B
{
public override int this[int x] { get { return 0; } set { } }
[IndexerName(""A"")] //error since name isn't copied down to override
public int this[int x, int y] { get { return 0; } set { } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (15,16): error CS0668: Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type
Diagnostic(ErrorCode.ERR_InconsistentIndexerNames, "this"));
}
[Fact]
public void NameNotCopiedOnOverride2()
{
var source = @"
using System.Runtime.CompilerServices;
class B
{
[IndexerName(""A"")]
public virtual int this[int x] { get { return 0; } set { } }
}
class D : B
{
[IndexerName(""A"")] //dev10 didn't allow this, but it should eliminate the error
public override int this[int x] { get { return 0; } set { } }
[IndexerName(""A"")] //error since name isn't copied down to override
public int this[int x, int y] { get { return 0; } set { } }
}
";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var derivedType = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("D");
Assert.True(derivedType.Indexers.All(i => i.MetadataName == "A"));
}
[Fact]
public void NameNotCopiedOnOverride3()
{
var source = @"
using System.Runtime.CompilerServices;
class B
{
[IndexerName(""A"")]
public virtual int this[int x] { get { return 0; } set { } }
}
class D : B
{
public override int this[int x] { get { return 0; } set { } }
// If the name of the overridden indexer was copied, this would be an error.
public int this[int x, int y] { get { return 0; } set { } }
}
";
CreateCompilation(source).VerifyDiagnostics();
}
[Fact]
public void IndexerNameLookup1()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
public const string get_X = ""X"";
}
class B : A
{
[IndexerName(C.get_X)]
public int this[int x] { get { return 0; } }
}
class C : B
{
[IndexerName(get_X)]
public int this[int x, int y] { get { return 0; } }
}
";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
var classA = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("A");
var classB = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("B");
var classC = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var get_XA = classA.GetMember<FieldSymbol>("get_X");
var get_XB = classB.GetMember<MethodSymbol>("get_X");
var get_XC = classC.GetMember<MethodSymbol>("get_X");
Assert.Equal("X", get_XB.AssociatedSymbol.MetadataName);
Assert.Equal("X", get_XC.AssociatedSymbol.MetadataName);
}
[Fact]
public void IndexerNameLookup2()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
public const string get_X = ""X"";
[IndexerName(get_X)]
public int this[int x] { get { return 0; } }
}
";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (9,30): error CS0102: The type 'A' already contains a definition for 'get_X'
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "get").WithArguments("A", "get_X"));
var classA = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("A");
Assert.Equal("X", classA.Indexers.Single().MetadataName);
}
[Fact]
public void IndexerNameLookup3()
{
var source = @"
using System.Runtime.CompilerServices;
public class MyAttribute : System.Attribute
{
public MyAttribute(object o) { }
}
class A
{
[IndexerName(get_Item)]
public int this[int x] { get { return 0; } }
// Doesn't matter what attribute it is or what member it's on - can't see indexer members.
[MyAttribute(get_Item)]
int x;
}
";
// NOTE: Dev10 reports CS0571 for MyAttribute's use of get_Item
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (11,18): error CS0571: 'A.this[int].get': cannot explicitly call operator or accessor
// [IndexerName(get_Item)]
Diagnostic(ErrorCode.ERR_CantCallSpecialMethod, "get_Item").WithArguments("A.this[int].get"),
// (15,18): error CS0571: 'A.this[int].get': cannot explicitly call operator or accessor
// [MyAttribute(get_Item)]
Diagnostic(ErrorCode.ERR_CantCallSpecialMethod, "get_Item").WithArguments("A.this[int].get"),
// (16,9): warning CS0169: The field 'A.x' is never used
// int x;
Diagnostic(ErrorCode.WRN_UnreferencedField, "x").WithArguments("A.x"));
}
[Fact]
public void IndexerNameLookup4()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
[IndexerName(B.get_Item)]
public int this[int x] { get { return 0; } }
}
class B
{
[IndexerName(A.get_Item)]
public int this[int x] { get { return 0; } }
}
";
// NOTE: Dev10 reports CS0117 in A, but CS0571 in B
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics(
// (6,20): error CS0571: 'B.this[int].get': cannot explicitly call operator or accessor
// [IndexerName(B.get_Item)]
Diagnostic(ErrorCode.ERR_CantCallSpecialMethod, "get_Item").WithArguments("B.this[int].get"),
// (12,20): error CS0571: 'A.this[int].get': cannot explicitly call operator or accessor
// [IndexerName(A.get_Item)]
Diagnostic(ErrorCode.ERR_CantCallSpecialMethod, "get_Item").WithArguments("A.this[int].get"));
}
[Fact]
public void IndexerNameLookup5()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
public const string get_Item = ""X"";
}
class B : A
{
public const string C = get_Item;
[IndexerName(C)]
public int this[int x] { get { return 0; } }
}
";
var compilation = CreateCompilation(source);
compilation.VerifyDiagnostics();
}
[Fact]
public void IndexerNameLookupClass()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
public const string Constant1 = B.Constant1;
public const string Constant2 = B.Constant2;
}
class B
{
public const string Constant1 = ""X"";
public const string Constant2 = A.Constant2;
[IndexerName(A.Constant1)]
public int this[int x] { get { return 0; } }
[IndexerName(A.Constant2)]
public int this[long x] { get { return 0; } }
}
";
// CONSIDER: this cascading is a bit verbose.
CreateCompilation(source).VerifyDiagnostics(
// (18,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(A.Constant2)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "A.Constant2"),
// (7,25): error CS0110: The evaluation of the constant value for 'A.Constant2' involves a circular definition
// public const string Constant2 = B.Constant2;
Diagnostic(ErrorCode.ERR_CircConstValue, "Constant2").WithArguments("A.Constant2"),
// (19,16): error CS0668: Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type
// public int this[long x] { get { return 0; } }
Diagnostic(ErrorCode.ERR_InconsistentIndexerNames, "this"));
}
[Fact]
public void IndexerNameLookupStruct()
{
var source = @"
using System.Runtime.CompilerServices;
struct A
{
public const string Constant1 = B.Constant1;
public const string Constant2 = B.Constant2;
}
struct B
{
public const string Constant1 = ""X"";
public const string Constant2 = A.Constant2;
[IndexerName(A.Constant1)]
public int this[int x] { get { return 0; } }
[IndexerName(A.Constant2)]
public int this[long x] { get { return 0; } }
}
";
// CONSIDER: this cascading is a bit verbose.
CreateCompilation(source).VerifyDiagnostics(
// (18,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(A.Constant2)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "A.Constant2"),
// (13,25): error CS0110: The evaluation of the constant value for 'A.Constant2' involves a circular definition
// public const string Constant2 = A.Constant2;
Diagnostic(ErrorCode.ERR_CircConstValue, "Constant2").WithArguments("A.Constant2"),
// (19,16): error CS0668: Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type
// public int this[long x] { get { return 0; } }
Diagnostic(ErrorCode.ERR_InconsistentIndexerNames, "this"));
}
[Fact]
public void IndexerNameLookupInterface()
{
var source = @"
using System.Runtime.CompilerServices;
interface A
{
const string Constant1 = B.Constant1;
const string Constant2 = B.Constant2;
}
interface B
{
const string Constant1 = ""X"";
const string Constant2 = A.Constant2;
[IndexerName(A.Constant1)]
int this[int x] { get; }
[IndexerName(A.Constant2)]
int this[long x] { get; }
}
";
// CONSIDER: this cascading is a bit verbose.
CreateCompilation(source, parseOptions: TestOptions.Regular7, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics(
// (18,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(A.Constant2)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "A.Constant2").WithLocation(18, 18),
// (7,18): error CS0110: The evaluation of the constant value for 'A.Constant2' involves a circular definition
// const string Constant2 = B.Constant2;
Diagnostic(ErrorCode.ERR_CircConstValue, "Constant2").WithArguments("A.Constant2").WithLocation(7, 18),
// (19,9): error CS0668: Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type
// int this[long x] { get; }
Diagnostic(ErrorCode.ERR_InconsistentIndexerNames, "this").WithLocation(19, 9),
// (12,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater.
// const string Constant1 = "X";
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Constant1").WithArguments("default interface implementation", "8.0").WithLocation(12, 18),
// (13,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater.
// const string Constant2 = A.Constant2;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Constant2").WithArguments("default interface implementation", "8.0").WithLocation(13, 18),
// (6,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater.
// const string Constant1 = B.Constant1;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Constant1").WithArguments("default interface implementation", "8.0").WithLocation(6, 18),
// (7,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater.
// const string Constant2 = B.Constant2;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Constant2").WithArguments("default interface implementation", "8.0").WithLocation(7, 18)
);
}
[Fact]
public void IndexerNameLookupGenericClass()
{
var source = @"
using System.Runtime.CompilerServices;
class A<T>
{
public const string Constant1 = B<string>.Constant1;
public const string Constant2 = B<int>.Constant2;
[IndexerName(B<byte>.Constant2)]
public int this[long x] { get { return 0; } }
}
class B<T>
{
public const string Constant1 = ""X"";
public const string Constant2 = A<bool>.Constant2;
[IndexerName(A<char>.Constant1)]
public int this[int x] { get { return 0; } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (9,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(B<byte>.Constant2)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "B<byte>.Constant2"),
// (7,25): error CS0110: The evaluation of the constant value for 'A<T>.Constant2' involves a circular definition
// public const string Constant2 = B<int>.Constant2;
Diagnostic(ErrorCode.ERR_CircConstValue, "Constant2").WithArguments("A<T>.Constant2"));
}
[Fact]
public void IndexerNameLookupGenericStruct()
{
var source = @"
using System.Runtime.CompilerServices;
struct A<T>
{
public const string Constant1 = B<string>.Constant1;
public const string Constant2 = B<int>.Constant2;
[IndexerName(B<byte>.Constant2)]
public int this[long x] { get { return 0; } }
}
struct B<T>
{
public const string Constant1 = ""X"";
public const string Constant2 = A<bool>.Constant2;
[IndexerName(A<char>.Constant1)]
public int this[int x] { get { return 0; } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (9,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(B<byte>.Constant2)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "B<byte>.Constant2"),
// (7,25): error CS0110: The evaluation of the constant value for 'A<T>.Constant2' involves a circular definition
// public const string Constant2 = B<int>.Constant2;
Diagnostic(ErrorCode.ERR_CircConstValue, "Constant2").WithArguments("A<T>.Constant2"));
}
[Fact]
public void IndexerNameLookupGenericInterface()
{
var source = @"
using System.Runtime.CompilerServices;
interface A<T>
{
const string Constant1 = B<string>.Constant1;
const string Constant2 = B<int>.Constant2;
[IndexerName(B<byte>.Constant2)]
int this[long x] { get; }
}
interface B<T>
{
const string Constant1 = ""X"";
const string Constant2 = A<bool>.Constant2;
[IndexerName(A<char>.Constant1)]
int this[int x] { get; }
}
";
CreateCompilation(source, parseOptions: TestOptions.Regular7, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics(
// (9,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(B<byte>.Constant2)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "B<byte>.Constant2").WithLocation(9, 18),
// (7,18): error CS0110: The evaluation of the constant value for 'A<T>.Constant2' involves a circular definition
// const string Constant2 = B<int>.Constant2;
Diagnostic(ErrorCode.ERR_CircConstValue, "Constant2").WithArguments("A<T>.Constant2").WithLocation(7, 18),
// (15,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater.
// const string Constant1 = "X";
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Constant1").WithArguments("default interface implementation", "8.0").WithLocation(15, 18),
// (16,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater.
// const string Constant2 = A<bool>.Constant2;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Constant2").WithArguments("default interface implementation", "8.0").WithLocation(16, 18),
// (6,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater.
// const string Constant1 = B<string>.Constant1;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Constant1").WithArguments("default interface implementation", "8.0").WithLocation(6, 18),
// (7,18): error CS8652: The feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater.
// const string Constant2 = B<int>.Constant2;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Constant2").WithArguments("default interface implementation", "8.0").WithLocation(7, 18)
);
}
[Fact]
public void IndexerNameLookupTypeParameter()
{
var source = @"
using System.Runtime.CompilerServices;
class P
{
public const string Constant1 = Q.Constant1;
public const string Constant2 = Q.Constant2;
}
class Q
{
public const string Constant1 = ""X"";
public const string Constant2 = P.Constant2;
}
class A<T> where T : P
{
[IndexerName(T.Constant1)]
public int this[long x] { get { return 0; } }
}
class B<T> where T : Q
{
[IndexerName(T.Constant2)]
public int this[long x] { get { return 0; } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (7,25): error CS0110: The evaluation of the constant value for 'P.Constant2' involves a circular definition
// public const string Constant2 = Q.Constant2;
Diagnostic(ErrorCode.ERR_CircConstValue, "Constant2").WithArguments("P.Constant2"),
// (18,18): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter
// [IndexerName(T.Constant1)]
Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(18, 18),
// (24,18): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter
// [IndexerName(T.Constant2)]
Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(24, 18));
}
[Fact]
public void IndexerNameLookupEnum()
{
var source = @"
using System.Runtime.CompilerServices;
enum E
{
A,
B,
C = 6,
D,
E = F,
F = E
}
class A
{
[IndexerName(E.A)]
public int this[long x] { get { return 0; } }
[IndexerName(E.B)]
public int this[char x] { get { return 0; } }
[IndexerName(E.C)]
public int this[bool x] { get { return 0; } }
[IndexerName(E.D)]
public int this[uint x] { get { return 0; } }
[IndexerName(E.E)]
public int this[byte x] { get { return 0; } }
[IndexerName(E.F)]
public int this[ulong x] { get { return 0; } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (10,5): error CS0110: The evaluation of the constant value for 'E.E' involves a circular definition
// E = F,
Diagnostic(ErrorCode.ERR_CircConstValue, "E").WithArguments("E.E"),
// (16,18): error CS1503: Argument 1: cannot convert from 'E' to 'string'
// [IndexerName(E.A)]
Diagnostic(ErrorCode.ERR_BadArgType, "E.A").WithArguments("1", "E", "string"),
// (19,18): error CS1503: Argument 1: cannot convert from 'E' to 'string'
// [IndexerName(E.B)]
Diagnostic(ErrorCode.ERR_BadArgType, "E.B").WithArguments("1", "E", "string"),
// (22,18): error CS1503: Argument 1: cannot convert from 'E' to 'string'
// [IndexerName(E.C)]
Diagnostic(ErrorCode.ERR_BadArgType, "E.C").WithArguments("1", "E", "string"),
// (25,18): error CS1503: Argument 1: cannot convert from 'E' to 'string'
// [IndexerName(E.D)]
Diagnostic(ErrorCode.ERR_BadArgType, "E.D").WithArguments("1", "E", "string"),
// (28,18): error CS1503: Argument 1: cannot convert from 'E' to 'string'
// [IndexerName(E.E)]
Diagnostic(ErrorCode.ERR_BadArgType, "E.E").WithArguments("1", "E", "string"),
// (31,18): error CS1503: Argument 1: cannot convert from 'E' to 'string'
// [IndexerName(E.F)]
Diagnostic(ErrorCode.ERR_BadArgType, "E.F").WithArguments("1", "E", "string"));
}
[Fact]
public void IndexerNameLookupProperties()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
internal static string Name { get { return ""A""; } }
[IndexerName(B.Name)]
public int this[int x] { get { return 0; } }
}
class B
{
internal static string Name { get { return ""B""; } }
[IndexerName(A.Name)]
public int this[int x] { get { return 0; } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (13,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(A.Name)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "A.Name"),
// (7,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(B.Name)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "B.Name"));
}
[Fact]
public void IndexerNameLookupCalls()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
internal static string GetName() { return ""A""; }
[IndexerName(B.GetName())]
public int this[int x] { get { return 0; } }
}
class B
{
internal static string GetName() { return ""B""; }
[IndexerName(A.GetName())]
public int this[int x] { get { return 0; } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (7,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(B.GetName())]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "B.GetName()"),
// (13,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(A.GetName())]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "A.GetName()"));
}
[Fact]
public void IndexerNameLookupNonExistent()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
[IndexerName(B.Fake)]
public int this[int x] { get { return 0; } }
}
class B
{
[IndexerName(A.Fake)]
public int this[int x] { get { return 0; } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (11,20): error CS0117: 'A' does not contain a definition for 'Fake'
// [IndexerName(A.Fake)]
Diagnostic(ErrorCode.ERR_NoSuchMember, "Fake").WithArguments("A", "Fake"),
// (6,20): error CS0117: 'B' does not contain a definition for 'Fake'
// [IndexerName(B.Fake)]
Diagnostic(ErrorCode.ERR_NoSuchMember, "Fake").WithArguments("B", "Fake"));
}
[Fact]
public void IndexerNameNotEmitted()
{
var source = @"
using System.Runtime.CompilerServices;
class Program
{
[IndexerName(""A"")]
public int this[int x]
{
get { return 0; }
set { }
}
}
";
var compilation = CreateCompilation(source).VerifyDiagnostics();
var indexer = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Program").Indexers.Single();
Assert.True(indexer.IsIndexer);
Assert.Equal("A", indexer.MetadataName);
Assert.True(indexer.GetAttributes().Single().IsTargetAttribute(AttributeDescription.IndexerNameAttribute));
CompileAndVerify(compilation, symbolValidator: module =>
{
var peIndexer = (PEPropertySymbol)module.GlobalNamespace.GetTypeMember("Program").Indexers.Single();
Assert.True(peIndexer.IsIndexer);
Assert.Equal("A", peIndexer.MetadataName);
Assert.Empty(peIndexer.GetAttributes());
Assert.Empty(((PEModuleSymbol)module).GetCustomAttributesForToken(peIndexer.Handle));
});
}
[WorkItem(545884, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545884")]
[Fact]
public void IndexerNameDeadlock1()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
public const string Name = ""A"";
[IndexerName(B.Name)]
public int this[int x] { get { return 0; } }
}
class B
{
public const string Name = ""B"";
[IndexerName(A.Name)]
public int this[int x] { get { return 0; } }
}
";
var compilation = CreateCompilation(source);
var loopResult = Parallel.ForEach(compilation.GlobalNamespace.GetTypeMembers(), type =>
type.ForceComplete(null, filter: null, default(CancellationToken)));
Assert.True(loopResult.IsCompleted);
compilation.VerifyDiagnostics();
}
[WorkItem(545884, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545884")]
[Fact]
public void IndexerNameDeadlock2()
{
var source = @"
using System.Runtime.CompilerServices;
class A
{
private const string Name = ""A"";
[IndexerName(B.Name)]
public int this[int x] { get { return 0; } }
}
class B
{
private const string Name = ""B"";
[IndexerName(A.Name)]
public int this[int x] { get { return 0; } }
}
";
var compilation = CreateCompilation(source);
var loopResult = Parallel.ForEach(compilation.GlobalNamespace.GetTypeMembers(), type =>
type.ForceComplete(null, filter: null, default(CancellationToken)));
Assert.True(loopResult.IsCompleted);
compilation.VerifyDiagnostics(
// (7,20): error CS0122: 'B.Name' is inaccessible due to its protection level
// [IndexerName(B.Name)]
Diagnostic(ErrorCode.ERR_BadAccess, "Name").WithArguments("B.Name"),
// (14,20): error CS0122: 'A.Name' is inaccessible due to its protection level
// [IndexerName(A.Name)]
Diagnostic(ErrorCode.ERR_BadAccess, "Name").WithArguments("A.Name"));
}
[Fact]
public void OverloadResolutionPrecedence()
{
var source =
@"public class C
{
public int this[int x] { get { return 0; } }
public int this[int x, int y = 1] { get { return 0; } }
public int this[params int[] x] { get { return 0; } }
void Method()
{
int x;
x = this[1];
x = this[1, 2];
x = this[1, 2, 3];
x = this[new int[1]];
}
}";
var tree = Parse(source);
var comp = CreateCompilation(tree);
comp.VerifyDiagnostics();
var model = comp.GetSemanticModel(tree);
CheckOverloadResolutionResults(tree, model,
"System.Int32 C.this[System.Int32 x] { get; }",
"System.Int32 C.this[System.Int32 x, [System.Int32 y = 1]] { get; }",
"System.Int32 C.this[params System.Int32[] x] { get; }",
"System.Int32 C.this[params System.Int32[] x] { get; }");
}
[Fact]
public void OverloadResolutionOverriding()
{
var source =
@"public class Base
{
public virtual int this[int x] { get { return 0; } }
public virtual int this[int x, int y = 1] { get { return 0; } }
public virtual int this[params int[] x] { get { return 0; } }
}
public class Derived : Base
{
public override int this[int x] { get { return 0; } }
public override int this[int x, int y = 1] { get { return 0; } }
public override int this[params int[] x] { get { return 0; } }
void Method()
{
int x;
x = this[1];
x = this[1, 2];
x = this[1, 2, 3];
x = base[1];
x = base[1, 2];
x = base[1, 2, 3];
}
}";
var tree = Parse(source);
var comp = CreateCompilation(tree);
comp.VerifyDiagnostics();
var model = comp.GetSemanticModel(tree);
CheckOverloadResolutionResults(tree, model,
// NOTE: we'll actually emit calls to the corresponding base indexers
"System.Int32 Derived.this[System.Int32 x] { get; }",
"System.Int32 Derived.this[System.Int32 x, [System.Int32 y = 1]] { get; }",
"System.Int32 Derived.this[params System.Int32[] x] { get; }",
"System.Int32 Base.this[System.Int32 x] { get; }",
"System.Int32 Base.this[System.Int32 x, [System.Int32 y = 1]] { get; }",
"System.Int32 Base.this[params System.Int32[] x] { get; }");
}
[Fact]
public void OverloadResolutionFallbackInBase()
{
var source =
@"public class Base
{
public int this[params int[] x] { get { return 0; } }
}
public class Derived : Base
{
public int this[int x] { get { return 0; } }
public int this[int x, int y = 1] { get { return 0; } }
void Method()
{
int x;
x = this[1];
x = this[1, 2];
x = this[1, 2, 3];
x = base[1];
x = base[1, 2];
x = base[1, 2, 3];
}
}";
var tree = Parse(source);
var comp = CreateCompilation(tree);
comp.VerifyDiagnostics();
var model = comp.GetSemanticModel(tree);
CheckOverloadResolutionResults(tree, model,
"System.Int32 Derived.this[System.Int32 x] { get; }",
"System.Int32 Derived.this[System.Int32 x, [System.Int32 y = 1]] { get; }",
"System.Int32 Base.this[params System.Int32[] x] { get; }",
"System.Int32 Base.this[params System.Int32[] x] { get; }",
"System.Int32 Base.this[params System.Int32[] x] { get; }",
"System.Int32 Base.this[params System.Int32[] x] { get; }");
}
[Fact]
public void OverloadResolutionDerivedRemovesParamsModifier()
{
var source =
@"abstract class Base
{
public abstract int this[Derived c1, Derived c2, params Derived[] c3] { get; }
}
class Derived : Base
{
public override int this[Derived C1, Derived C2, Derived[] C3] { get { return 0; } } //removes 'params'
}
class Test2
{
public static void Main2()
{
Derived d = new Derived();
Base b = d;
int x;
x = b[d, d, d, d, d]; // Fine
x = d[d, d, d, d, d]; // Fine
}
}";
var tree = Parse(source);
var comp = CreateCompilation(tree);
comp.VerifyDiagnostics();
var model = comp.GetSemanticModel(tree);
CheckOverloadResolutionResults(tree, model,
"System.Int32 Base.this[Derived c1, Derived c2, params Derived[] c3] { get; }",
"System.Int32 Derived.this[Derived C1, Derived C2, params Derived[] C3] { get; }");
}
[Fact]
public void OverloadResolutionDerivedAddsParamsModifier()
{
var source =
@"abstract class Base
{
public abstract int this[Derived c1, Derived c2, Derived[] c3] { get; }
}
class Derived : Base
{
public override int this[Derived C1, Derived C2, params Derived[] C3] { get { return 0; } } //adds 'params'
}
class Test2
{
public static void Main2()
{
Derived d = new Derived();
Base b = d;
int x;
x = b[d, d, d, d, d]; // CS1501
x = d[d, d, d, d, d]; // CS1501
}
}";
CreateCompilation(source).VerifyDiagnostics(
// (16,13): error CS1501: No overload for method 'this' takes 5 arguments
Diagnostic(ErrorCode.ERR_BadArgCount, "b[d, d, d, d, d]").WithArguments("this", "5"),
// (17,13): error CS1501: No overload for method 'this' takes 5 arguments
Diagnostic(ErrorCode.ERR_BadArgCount, "d[d, d, d, d, d]").WithArguments("this", "5"));
}
[WorkItem(542747, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542747")]
[Fact()]
public void IndexerAccessorParameterIsSynthesized()
{
var text = @"
struct Test
{
public byte this[byte p] { get { return p; } }
}
";
var comp = CreateCompilation(text);
NamedTypeSymbol type01 = comp.SourceModule.GlobalNamespace.GetTypeMembers("Test").Single();
var indexer = type01.GetMembers(WellKnownMemberNames.Indexer).Single() as PropertySymbol;
Assert.NotNull(indexer.GetMethod);
Assert.False(indexer.GetMethod.Parameters.IsEmpty);
// VB is SynthesizedParameterSymbol; C# is SourceComplexParameterSymbol
foreach (var p in indexer.GetMethod.Parameters)
{
Assert.True(p.IsImplicitlyDeclared, "Parameter of Indexer Accessor");
}
}
[WorkItem(542831, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542831")]
[Fact]
public void ProtectedBaseIndexer()
{
var text = @"
public class Base
{
protected int this[int index] { get { return 0; } }
}
public class Derived : Base
{
public int M()
{
return base[0];
}
}
";
CreateCompilation(text).VerifyDiagnostics();
}
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void SameSignaturesDifferentNames()
{
var ilSource = @"
.class public auto ansi beforefieldinit SameSignaturesDifferentNames
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string)
= {string('Accessor1')}
.method public hidebysig specialname instance int32
Accessor1(int32 x, int64 y) cil managed
{
ldc.i4.0
ret
}
.method public hidebysig specialname instance void
Accessor2(int32 x, int64 y,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname instance void
Accessor3(int32 x, int64 y,
int32 'value') cil managed
{
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.property instance int32 Indexer1(int32, int64)
{
.get instance int32 SameSignaturesDifferentNames::Accessor1(int32, int64)
.set instance void SameSignaturesDifferentNames::Accessor2(int32, int64, int32)
}
.property instance int32 Indexer2(int32, int64)
{
.get instance int32 SameSignaturesDifferentNames::Accessor1(int32, int64)
.set instance void SameSignaturesDifferentNames::Accessor3(int32, int64, int32)
}
}";
var cSharpSource = @"
class Test
{
static void Main()
{
SameSignaturesDifferentNames s = new SameSignaturesDifferentNames();
System.Console.WriteLine(s[0, 1]);
}
}
";
CreateCompilationWithILAndMscorlib40(cSharpSource, ilSource).VerifyDiagnostics(
// (7,34): error CS0121: The call is ambiguous between the following methods or properties: 'SameSignaturesDifferentNames.this[int, long]' and 'SameSignaturesDifferentNames.this[int, long]'
Diagnostic(ErrorCode.ERR_AmbigCall, "s[0, 1]").WithArguments("SameSignaturesDifferentNames.this[int, long]", "SameSignaturesDifferentNames.this[int, long]"));
}
[WorkItem(543261, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543261")]
[ClrOnlyFact]
public void OverrideOneAccessorOnly()
{
var source =
@"class A
{
public virtual object this[object index] { get { return null; } set { } }
}
class B1 : A
{
public override object this[object index] { get { return base[index]; } }
}
class B2 : A
{
public override object this[object index] { set { base[index] = value; } }
}
class C
{
static void M(B1 _1, B2 _2)
{
_1[null] = _1[null];
_2[null] = _2[null];
}
}";
CompileAndVerify(source);
}
private static void CheckOverloadResolutionResults(SyntaxTree tree, SemanticModel model, params string[] expected)
{
var actual = GetElementAccessExpressions(tree.GetCompilationUnitRoot()).Select(syntax => model.GetSymbolInfo(syntax).Symbol.ToTestDisplayString());
AssertEx.Equal(expected, actual, itemInspector: s => string.Format("\"{0}\"", s));
}
private static IEnumerable<ElementAccessExpressionSyntax> GetElementAccessExpressions(SyntaxNode node)
{
return node == null ?
SpecializedCollections.EmptyEnumerable<ElementAccessExpressionSyntax>() :
node.DescendantNodesAndSelf().Where(s => s.IsKind(SyntaxKind.ElementAccessExpression)).Cast<ElementAccessExpressionSyntax>();
}
[Fact]
public void PartialType()
{
var text1 = @"
partial class C
{
public int this[int x] { get { return 0; } set { } }
}";
var text2 = @"
partial class C
{
public void M() {}
}
";
var compilation = CreateCompilation(new string[] { text1, text2 });
Assert.True(((TypeSymbol)compilation.GlobalNamespace.GetTypeMembers("C").Single()).GetMembers().Any(x => x.IsIndexer()));
//test with text inputs reversed in case syntax ordering predicate ever changes.
compilation = CreateCompilation(new string[] { text2, text1 });
Assert.True(((TypeSymbol)compilation.GlobalNamespace.GetTypeMembers("C").Single()).GetMembers().Any(x => x.IsIndexer()));
}
[WorkItem(543957, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543957")]
[Fact]
public void SemanticModelIndexerGroupHiding()
{
var source =
@"public class Base
{
public int this[int x] { get { return x; } }
public virtual int this[int x, int y] { get { return x; } }
public int this[int x, int y, int z] { get { return x; } }
}
public class Derived : Base
{
public new int this[int x] { get { return x; } }
public override int this[int x, int y] { get { return x; } }
void Method()
{
int x;
x = this[1];
x = base[1];
Derived d = new Derived();
x = d[1];
Base b = new Base();
x = b[1];
Wrapper w = new Wrapper();
x = w.Base[1];
x = w.Derived[1];
x = (d ?? w.Derived)[1];
}
}
public class Wrapper
{
public Base Base;
public Derived Derived;
}
";
var tree = Parse(source);
var comp = CreateCompilation(tree);
comp.VerifyDiagnostics();
var model = comp.GetSemanticModel(tree);
var elementAccessSyntaxes = GetElementAccessExpressions(tree.GetCompilationUnitRoot());
// The access itself doesn't have an indexer group.
foreach (var syntax in elementAccessSyntaxes)
{
Assert.Equal(0, model.GetIndexerGroup(syntax).Length);
}
var baseType = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("Base");
var derivedType = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("Derived");
var baseIndexers = baseType.Indexers;
var derivedIndexers = derivedType.Indexers;
var baseIndexer3 = baseIndexers.Single(indexer => indexer.ParameterCount == 3);
var baseIndexerGroup = baseIndexers;
var derivedIndexerGroup = derivedIndexers.Concat(ImmutableArray.Create<PropertySymbol>(baseIndexer3));
var receiverSyntaxes = elementAccessSyntaxes.Select(access => access.Expression);
Assert.Equal(7, receiverSyntaxes.Count());
// The receiver of each access expression has an indexer group.
foreach (var syntax in receiverSyntaxes)
{
var type = model.GetTypeInfo(syntax).Type.GetSymbol();
Assert.NotNull(type);
var indexerGroup = model.GetIndexerGroup(syntax);
if (type.Equals(baseType))
{
Assert.True(indexerGroup.SetEquals(baseIndexerGroup.GetPublicSymbols(), EqualityComparer<IPropertySymbol>.Default));
}
else if (type.Equals(derivedType))
{
Assert.True(indexerGroup.SetEquals(derivedIndexerGroup.GetPublicSymbols(), EqualityComparer<IPropertySymbol>.Default));
}
else
{
Assert.True(false, "Unexpected type " + type.ToTestDisplayString());
}
}
}
[WorkItem(543957, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543957")]
[Fact]
public void SemanticModelIndexerGroupAccessibility()
{
var source =
@"class Base
{
private int this[int x] { get { return 0; } }
protected int this[string x] { get { return 0; } }
public int this[bool x] { get { return 0; } }
void M()
{
int x;
x = this[1]; //all
}
}
class Derived1 : Base
{
void M()
{
int x;
x = this[""string""]; //public and protected
Derived2 d = new Derived2();
x = d[true]; //only public
}
}
class Derived2 : Base
{
}
";
var tree = Parse(source);
var comp = CreateCompilation(tree);
comp.VerifyDiagnostics();
var model = comp.GetSemanticModel(tree);
var elementAccessSyntaxes = GetElementAccessExpressions(tree.GetCompilationUnitRoot());
// The access itself doesn't have an indexer group.
foreach (var syntax in elementAccessSyntaxes)
{
Assert.Equal(0, model.GetIndexerGroup(syntax).Length);
}
var baseType = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("Base");
var derived1Type = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("Derived1");
var derived2Type = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("Derived2");
var indexers = baseType.Indexers;
var publicIndexer = indexers.Single(indexer => indexer.DeclaredAccessibility == Accessibility.Public);
var protectedIndexer = indexers.Single(indexer => indexer.DeclaredAccessibility == Accessibility.Protected);
var privateIndexer = indexers.Single(indexer => indexer.DeclaredAccessibility == Accessibility.Private);
var receiverSyntaxes = elementAccessSyntaxes.Select(access => access.Expression).ToArray();
Assert.Equal(3, receiverSyntaxes.Length);
// In declaring type, can see everything.
Assert.True(model.GetIndexerGroup(receiverSyntaxes[0]).SetEquals(
ImmutableArray.Create<PropertySymbol>(publicIndexer, protectedIndexer, privateIndexer).GetPublicSymbols(),
EqualityComparer<IPropertySymbol>.Default));
// In subtype of declaring type, can see non-private.
Assert.True(model.GetIndexerGroup(receiverSyntaxes[1]).SetEquals(
ImmutableArray.Create<PropertySymbol>(publicIndexer, protectedIndexer).GetPublicSymbols(),
EqualityComparer<IPropertySymbol>.Default));
// In subtype of declaring type, can only see public (or internal) members of other subtypes.
Assert.True(model.GetIndexerGroup(receiverSyntaxes[2]).SetEquals(
ImmutableArray.Create<PropertySymbol>(publicIndexer).GetPublicSymbols(),
EqualityComparer<IPropertySymbol>.Default));
}
[WorkItem(545851, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545851")]
[ClrOnlyFact(ClrOnlyReason.Ilasm)]
public void DistinctOptionalParameterValues()
{
var source1 =
@".class abstract public A
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = {string('P')}
.method public hidebysig specialname rtspecialname instance void .ctor()
{
ret
}
.method public abstract virtual instance int32 get_P(int32 x, [opt] int32 y)
{
.param[2] = int32(1)
}
.method public abstract virtual instance void set_P(int32 x, [opt] int32 y, int32 v)
{
.param[2] = int32(2)
}
.property instance int32 P(int32, int32)
{
.get instance int32 A::get_P(int32, int32)
.set instance void A::set_P(int32, int32, int32)
}
}";
var reference1 = CompileIL(source1);
var source2 =
@"using System;
class B : A
{
public override int this[int x, int y = 3]
{
get
{
Console.WriteLine(""get_P: {0}"", y);
return 0;
}
set
{
Console.WriteLine(""set_P: {0}"", y);
}
}
}
class C
{
static void Main()
{
B b = new B();
b[0] = b[0];
b[1] += 1;
A a = b;
a[0] = a[0];
a[1] += 1; // Dev11 uses get_P default for both
}
}";
var compilation2 = CompileAndVerify(source2, references: new[] { reference1 }, expectedOutput:
@"get_P: 3
set_P: 3
get_P: 3
set_P: 3
get_P: 1
set_P: 2
get_P: 1
set_P: 1");
}
[Fact, WorkItem(546255, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546255")]
public void RetargetingIndexerMetadataName()
{
#region "Source"
var src1 = @"using System;
public interface IGoo
{
int this[int i] { get; }
}
public class Goo : IGoo
{
public int this[int i] { get { return i; } }
}
";
var src2 = @"using System;
class Test
{
public void M()
{
IGoo igoo = new Goo();
var local = igoo[100];
}
}
";
#endregion
var comp1 = CreateEmptyCompilation(src1, new[] { Net40.References.mscorlib });
var comp2 = CreateCompilation(src2, new[] { new CSharpCompilationReference(comp1) });
var typeSymbol = comp1.SourceModule.GlobalNamespace.GetMember<NamedTypeSymbol>("IGoo");
var idxSymbol = typeSymbol.GetMember<PropertySymbol>(WellKnownMemberNames.Indexer);
Assert.NotNull(idxSymbol);
Assert.Equal("this[]", idxSymbol.Name);
Assert.Equal("Item", idxSymbol.MetadataName);
var tree = comp2.SyntaxTrees[0];
var model = comp2.GetSemanticModel(tree);
ExpressionSyntax expr = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ElementAccessExpressionSyntax>().FirstOrDefault();
var idxSymbol2 = model.GetSymbolInfo(expr);
Assert.NotNull(idxSymbol2.Symbol);
Assert.Equal(WellKnownMemberNames.Indexer, idxSymbol2.Symbol.Name);
Assert.Equal("Item", idxSymbol2.Symbol.MetadataName);
}
[Fact, WorkItem(546255, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546255")]
public void SubstitutedIndexerMetadataName()
{
var source = @"
class C<T>
{
int this[int x] { get { return 0; } }
}
";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics();
var unsubstitutedType = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var unsubstitutedIndexer = unsubstitutedType.GetMember<SourcePropertySymbol>(WellKnownMemberNames.Indexer);
Assert.Equal(WellKnownMemberNames.Indexer, unsubstitutedIndexer.Name);
Assert.Equal("Item", unsubstitutedIndexer.MetadataName);
var substitutedType = unsubstitutedType.Construct(comp.GetSpecialType(SpecialType.System_Int32));
var substitutedIndexer = substitutedType.GetMember<SubstitutedPropertySymbol>(WellKnownMemberNames.Indexer);
Assert.Equal(WellKnownMemberNames.Indexer, substitutedIndexer.Name);
Assert.Equal("Item", substitutedIndexer.MetadataName);
}
[Fact, WorkItem(806258, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/806258")]
public void ConflictWithTypeParameter()
{
var source = @"
class C<Item, get_Item>
{
int this[int x] { get { return 0; } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (4,9): error CS0102: The type 'C<Item, get_Item>' already contains a definition for 'Item'
// int this[int x] { get { return 0; } }
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "this").WithArguments("C<Item, get_Item>", "Item"),
// (4,23): error CS0102: The type 'C<Item, get_Item>' already contains a definition for 'get_Item'
// int this[int x] { get { return 0; } }
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "get").WithArguments("C<Item, get_Item>", "get_Item"));
}
[Fact, WorkItem(806258, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/806258")]
public void ConflictWithTypeParameter_IndexerNameAttribute()
{
var source = @"
using System.Runtime.CompilerServices;
class C<A, get_A>
{
[IndexerName(""A"")]
int this[int x] { get { return 0; } }
}
";
CreateCompilation(source).VerifyDiagnostics(
// (7,9): error CS0102: The type 'C<A, get_A>' already contains a definition for 'A'
// int this[int x] { get { return 0; } }
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "this").WithArguments("C<A, get_A>", "A"),
// (7,23): error CS0102: The type 'C<A, get_A>' already contains a definition for 'get_A'
// int this[int x] { get { return 0; } }
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "get").WithArguments("C<A, get_A>", "get_A"));
}
[Fact]
public void IndexerNameNoConstantValue()
{
var source =
@"using System.Runtime.CompilerServices;
class C
{
const string F;
[IndexerName(F)]
object this[object o] { get { return null; } }
}";
CreateCompilation(source).VerifyDiagnostics(
// (4,18): error CS0145: A const field requires a value to be provided
// const string F;
Diagnostic(ErrorCode.ERR_ConstValueRequired, "F").WithLocation(4, 18),
// (5,18): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [IndexerName(F)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "F").WithLocation(5, 18));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68110")]
public void DefaultSyntaxValueReentrancy_01()
{
var source =
"""
#nullable enable
[A(3, X = 6)]
public struct A
{
public int X;
public A(int x, A a = new A()[1]) { }
public int this[int i] { get => 0; set { } }
}
""";
var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp);
var a = compilation.GlobalNamespace.GetTypeMember("A").InstanceConstructors.Where(c => !c.IsDefaultValueTypeConstructor()).Single();
Assert.Null(a.Parameters[1].ExplicitDefaultValue);
Assert.True(a.Parameters[1].HasExplicitDefaultValue);
compilation.VerifyDiagnostics(
// (3,2): error CS0616: 'A' is not an attribute class
// [A(3, X = 6)]
Diagnostic(ErrorCode.ERR_NotAnAttributeClass, "A").WithArguments("A").WithLocation(3, 2),
// (3,2): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
// [A(3, X = 6)]
Diagnostic(ErrorCode.ERR_BadAttributeArgument, "A(3, X = 6)").WithLocation(3, 2),
// (8,27): error CS1736: Default parameter value for 'a' must be a compile-time constant
// public A(int x, A a = new A()[1]) { }
Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "new A()[1]").WithArguments("a").WithLocation(8, 27));
}
}
}
|