/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit3/Semantics/ParamsCollectionTests.cs
17 124 строки
658 KB
AlekseyTs
Adjust IL used for a null check in the compiler-synthesized inline-array helpers (#84523)
20 июл 2026, 18:31
Не верифицирован
20 июл 2026, 18:31
4d33ccf
Код
Авторство
О чём код?
// 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.Linq; 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.PooledObjects; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { public class ParamsCollectionTests : CompilingTestBase { private static string ParamCollectionAttributeSource => TestSources.ParamsCollectionAttribute; private static void VerifyParamsAndAttribute(ParameterSymbol parameter, bool isParamArray = false, bool isParamCollection = false) { VerifyParams(parameter, isParamArray: isParamArray, isParamCollection: isParamCollection); var peParameter = (PEParameterSymbol)parameter; PEModule module = ((PEModuleSymbol)peParameter.ContainingModule).Module; Assert.Equal(isParamArray, module.HasParamArrayAttribute(peParameter.Handle)); Assert.Equal(isParamCollection, module.HasParamCollectionAttribute(peParameter.Handle)); } private static void VerifyParams(ParameterSymbol parameter, bool isParamArray = false, bool isParamCollection = false) { Assert.Equal(isParamArray, parameter.IsParamsArray); Assert.Equal(isParamCollection, parameter.IsParamsCollection); Assert.Equal(isParamArray | isParamCollection, parameter.IsParams); IParameterSymbol iParameter = parameter.GetPublicSymbol(); Assert.Equal(isParamArray, iParameter.IsParamsArray); Assert.Equal(isParamCollection, iParameter.IsParamsCollection); Assert.Equal(isParamArray | isParamCollection, iParameter.IsParams); } private static void VerifyParams(IParameterSymbol parameter, bool isParamArray = false, bool isParamCollection = false) { VerifyParams(parameter.GetSymbol<ParameterSymbol>(), isParamArray: isParamArray, isParamCollection: isParamCollection); } private static string ExpectedOutput(string output) { return ExecutionConditionUtil.IsMonoOrCoreClr ? output : null; } [Fact] public void Span() { var src = @" class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params System.Span<long> a) { if (a.Length == 0) { System.Console.WriteLine(a.Length); } else { System.Console.WriteLine(""{0}: {1} ... {2}"", a.Length, a[0], a[^1]); } } static void Test2() { Test([2, 3]); } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.FailsILVerify with { ILVerifyMessage = InlineArrayTests.InlineArrayAsSpanILVerifyMessage + '\n' + InlineArrayTests.InlineArrayElementRefILVerifyMessage } : Verification.Skipped, sourceSymbolValidator: static (m) => { VerifyParams(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, symbolValidator: static (m) => { VerifyParamsAndAttribute(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, expectedOutput: ExpectedOutput(@" 0 1: 1 ... 1 2: 2 ... 3 ")).VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var nodes = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().Take(3).ToArray(); Assert.Equal("Test()", nodes[0].ToString()); comp.VerifyOperationTree(nodes[0], expectedOperationTree: """ IInvocationOperation (void Program.Test(params System.Span<System.Int64> a)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test()') Instance Receiver: null Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: a) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Test()') ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span<System.Int64>, IsImplicit) (Syntax: 'Test()') Elements(0) InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) """); Assert.Equal("Test(1)", nodes[1].ToString()); comp.VerifyOperationTree(nodes[1], expectedOperationTree: """ IInvocationOperation (void Program.Test(params System.Span<System.Int64> a)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test(1)') Instance Receiver: null Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: a) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Test(1)') ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span<System.Int64>, IsImplicit) (Syntax: 'Test(1)') Elements(1): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, Constant: 1, IsImplicit) (Syntax: '1') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) """); Assert.Equal("Test(2, 3)", nodes[2].ToString()); comp.VerifyOperationTree(nodes[2], expectedOperationTree: """ IInvocationOperation (void Program.Test(params System.Span<System.Int64> a)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test(2, 3)') Instance Receiver: null Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: a) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Test(2, 3)') ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span<System.Int64>, IsImplicit) (Syntax: 'Test(2, 3)') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, Constant: 2, IsImplicit) (Syntax: '2') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, Constant: 3, IsImplicit) (Syntax: '3') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) """); var argument = nodes[1].ArgumentList.Arguments[0].Expression; var model = comp.GetSemanticModel(tree); var typeInfo = model.GetTypeInfo(argument); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int64", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(argument).IsNumeric); } [Fact] public void Span_InAttribute() { var src = @" [Test()] class C1; [Test(1)] class C2; [Test(2, 3)] class C3; class Test : System.Attribute { public Test(params System.Span<long> a) {} } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (2,2): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Test.Test(params Span<long>)' // [Test()] Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test()").WithArguments("a", "Test.Test(params System.Span<long>)").WithLocation(2, 2), // (5,7): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Span<long>' // [Test(1)] Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Span<long>").WithLocation(5, 7), // (8,2): error CS1729: 'Test' does not contain a constructor that takes 2 arguments // [Test(2, 3)] Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test(2, 3)").WithArguments("Test", "2").WithLocation(8, 2) ); assertAttributeData("C1"); assertAttributeData("C2"); assertAttributeData("C3"); var tree = comp.SyntaxTrees.Single(); var nodes = tree.GetRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().ToArray(); Assert.Equal(3, nodes.Length); var model = comp.GetSemanticModel(tree); foreach (LiteralExpressionSyntax expression in nodes) { assertTypeInfo(expression); } void assertTypeInfo(LiteralExpressionSyntax expression) { var typeInfo = model.GetTypeInfo(expression); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(expression).IsIdentity); } void assertAttributeData(string name) { var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); Assert.True(attributeData1.HasErrors); Assert.Empty(attributeData1.ConstructorArguments); } } [Fact] public void ReadOnlySpan() { var src = @" class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params System.ReadOnlySpan<long> a) { if (a.Length == 0) { System.Console.WriteLine(a.Length); } else { System.Console.WriteLine(""{0}: {1} ... {2}"", a.Length, a[0], a[^1]); } } static void Test2() { Test([2, 3]); } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, sourceSymbolValidator: static (m) => { VerifyParams(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, symbolValidator: static (m) => { VerifyParamsAndAttribute(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, expectedOutput: ExpectedOutput(@" 0 1: 1 ... 1 2: 2 ... 3 ")).VerifyDiagnostics(); } [Fact] public void ReadOnlySpan_InAttribute() { var src = @" [Test()] class C1; [Test(1)] class C2; [Test(2, 3)] class C3; class Test : System.Attribute { public Test(params System.ReadOnlySpan<long> a) {} } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (2,2): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Test.Test(params ReadOnlySpan<long>)' // [Test()] Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test()").WithArguments("a", "Test.Test(params System.ReadOnlySpan<long>)").WithLocation(2, 2), // (5,7): error CS1503: Argument 1: cannot convert from 'int' to 'params System.ReadOnlySpan<long>' // [Test(1)] Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.ReadOnlySpan<long>").WithLocation(5, 7), // (8,2): error CS1729: 'Test' does not contain a constructor that takes 2 arguments // [Test(2, 3)] Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test(2, 3)").WithArguments("Test", "2").WithLocation(8, 2) ); assertAttributeData("C1"); assertAttributeData("C2"); assertAttributeData("C3"); var tree = comp.SyntaxTrees.Single(); var nodes = tree.GetRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().ToArray(); Assert.Equal(3, nodes.Length); var model = comp.GetSemanticModel(tree); foreach (LiteralExpressionSyntax expression in nodes) { assertTypeInfo(expression); } void assertTypeInfo(LiteralExpressionSyntax expression) { var typeInfo = model.GetTypeInfo(expression); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(expression).IsIdentity); } void assertAttributeData(string name) { var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); Assert.True(attributeData1.HasErrors); Assert.Empty(attributeData1.ConstructorArguments); } } [Fact] public void Span_SingleElement_TempsAreNotReused() { var source = """ using System; class Program { static void Main() { M(1); M(2); } static void M(params Span<int> span) { Console.Write(span[0]); Console.Write(span.Length); } } """; var verifier = CompileAndVerify( source, targetFramework: TargetFramework.Net80, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, expectedOutput: ExpectedOutput("1121")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 29 (0x1d) .maxstack 1 .locals init (int V_0, int V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: newobj "System.Span<int>..ctor(ref int)" IL_0009: call "void Program.M(params System.Span<int>)" IL_000e: ldc.i4.2 IL_000f: stloc.1 IL_0010: ldloca.s V_1 IL_0012: newobj "System.Span<int>..ctor(ref int)" IL_0017: call "void Program.M(params System.Span<int>)" IL_001c: ret } """); verifier = CompileAndVerify( source, targetFramework: TargetFramework.Net70, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, expectedOutput: ExpectedOutput("1121")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 41 (0x29) .maxstack 4 IL_0000: ldc.i4.1 IL_0001: newarr "int" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: stelem.i4 IL_000a: newobj "System.Span<int>..ctor(int[])" IL_000f: call "void Program.M(params System.Span<int>)" IL_0014: ldc.i4.1 IL_0015: newarr "int" IL_001a: dup IL_001b: ldc.i4.0 IL_001c: ldc.i4.2 IL_001d: stelem.i4 IL_001e: newobj "System.Span<int>..ctor(int[])" IL_0023: call "void Program.M(params System.Span<int>)" IL_0028: ret } """); } [Fact] public void String() { var src = @" class Program { static void Main() { Test(); Test('a'); Test('b', 'c'); } static void Test(params string a) { } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (6,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params string)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params string)").WithLocation(6, 9), // (7,14): error CS1503: Argument 1: cannot convert from 'char' to 'params string' // Test('a'); Diagnostic(ErrorCode.ERR_BadArgType, "'a'").WithArguments("1", "char", "params string").WithLocation(7, 14), // (8,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test('b', 'c'); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(8, 9), // (11,22): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void Test(params string a) Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string a").WithArguments("string", "0").WithLocation(11, 22) ); } [Fact] public void String_InAttribute() { var src = @" [Test()] class C1; [Test('1')] class C2; [Test('2', '3')] class C3; class Test : System.Attribute { public Test(params string a) {} } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (2,2): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Test.Test(params string)' // [Test()] Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test()").WithArguments("a", "Test.Test(params string)").WithLocation(2, 2), // (5,7): error CS1503: Argument 1: cannot convert from 'char' to 'params string' // [Test('1')] Diagnostic(ErrorCode.ERR_BadArgType, "'1'").WithArguments("1", "char", "params string").WithLocation(5, 7), // (8,2): error CS1729: 'Test' does not contain a constructor that takes 2 arguments // [Test('2', '3')] Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test('2', '3')").WithArguments("Test", "2").WithLocation(8, 2), // (13,17): error CS1729: 'string' does not contain a constructor that takes 0 arguments // public Test(params string a) {} Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string a").WithArguments("string", "0").WithLocation(13, 17) ); assertAttributeData("C1"); assertAttributeData("C2"); assertAttributeData("C3"); var tree = comp.SyntaxTrees.Single(); var nodes = tree.GetRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().ToArray(); Assert.Equal(3, nodes.Length); var model = comp.GetSemanticModel(tree); foreach (LiteralExpressionSyntax expression in nodes) { assertTypeInfo(expression); } void assertTypeInfo(LiteralExpressionSyntax expression) { var typeInfo = model.GetTypeInfo(expression); Assert.Equal("System.Char", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Char", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(expression).IsIdentity); } void assertAttributeData(string name) { var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); Assert.True(attributeData1.HasErrors); Assert.Empty(attributeData1.ConstructorArguments); } } [Fact] public void CreateMethod_01() { var src = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection { public long[] Array; public IEnumerator<long> GetEnumerator() => throw null; } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<long> items) => new MyCollection() { Array = items.ToArray() }; } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { if (a.Array.Length == 0) { System.Console.WriteLine(a.Array.Length); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Length, a.Array[0], a.Array[^1]); } } static void Test2() { Test([2, 3]); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, sourceSymbolValidator: static (m) => { VerifyParams(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, symbolValidator: static (m) => { VerifyParamsAndAttribute(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, expectedOutput: ExpectedOutput(@" 0 1: 1 ... 1 2: 2 ... 3 ")).VerifyDiagnostics(); } [Theory] [CombinatorialData] public void CreateMethod_02_InAttribute(bool asStruct) { var src = @" using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] " + (asStruct ? "struct" : "class") + @" MyCollection { public IEnumerator<long> GetEnumerator() => throw new InvalidOperationException(); } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<long> items) => new MyCollection(); } [Test()] class C1; [Test(1)] class C2; [Test(2, 3)] class C3; class Test : System.Attribute { public Test(params MyCollection a) {} } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (16,2): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Test.Test(params MyCollection)' // [Test()] Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test()").WithArguments("a", "Test.Test(params MyCollection)").WithLocation(16, 2), // (19,7): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // [Test(1)] Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(19, 7), // (22,2): error CS1729: 'Test' does not contain a constructor that takes 2 arguments // [Test(2, 3)] Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test(2, 3)").WithArguments("Test", "2").WithLocation(22, 2) ); assertAttributeData("C1"); assertAttributeData("C2"); assertAttributeData("C3"); var tree = comp.SyntaxTrees.Single(); var nodes = tree.GetRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().ToArray(); Assert.Equal(3, nodes.Length); var model = comp.GetSemanticModel(tree); foreach (LiteralExpressionSyntax expression in nodes) { assertTypeInfo(expression); } void assertTypeInfo(LiteralExpressionSyntax expression) { var typeInfo = model.GetTypeInfo(expression); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(expression).IsIdentity); } void assertAttributeData(string name) { var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); Assert.True(attributeData1.HasErrors); Assert.Empty(attributeData1.ConstructorArguments); } } [Fact] public void CreateMethod_03_NoElementType() { var src = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection { } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<long> items) => null; } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (18,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(18, 9), // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14), // (20,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(20, 9), // (23,22): error CS0225: The params parameter must have a valid collection type // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(23, 22) ); } [Fact] public void CreateMethod_04_NoElementType_ExtensionGetEnumerator() { var src = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection { } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<long> items) => null; } class Program { static void Main() { Test(); Test(1); Test(2, 3); new MyCollection().GetEnumerator(); } static void Test(params MyCollection a) { foreach (var x in a) { long y = x; } } } static class Ext { public static IEnumerator<long> GetEnumerator(this MyCollection x) => throw null; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (18,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(18, 9), // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14), // (20,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(20, 9), // (23,22): error CS0225: The params parameter must have a valid collection type // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(23, 22) ); } [Fact] public void CreateMethod_05_Missing() { var src = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public class MyCollection { public IEnumerator<long> GetEnumerator() => throw null; } public class MyCollectionBuilder { } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( // (18,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // Test(); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test()").WithArguments("Create", "long", "MyCollection").WithLocation(18, 9), // (19,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // Test(1); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(1)").WithArguments("Create", "long", "MyCollection").WithLocation(19, 9), // (20,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // Test(2, 3); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(2, 3)").WithArguments("Create", "long", "MyCollection").WithLocation(20, 9), // (23,29): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // public static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "params MyCollection a").WithArguments("Create", "long", "MyCollection").WithLocation(23, 29) ); } [Fact] public void CreateMethod_06_InconsistentAccessibility() { var src = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection { public IEnumerator<long> GetEnumerator() => throw null; } public class MyCollectionBuilder { internal static MyCollection Create(ReadOnlySpan<long> items) => null; } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( // (24,29): error CS9224: Method 'MyCollectionBuilder.Create(ReadOnlySpan<long>)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. // public static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollectionBuilder.Create(System.ReadOnlySpan<long>)", "Program.Test(params MyCollection)").WithLocation(24, 29) ); } [Fact] public void CreateMethod_07_InconsistentAccessibility() { var src = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder1), nameof(MyCollectionBuilder1.Create))] public class MyCollection1 { public IEnumerator<long> GetEnumerator() => throw null; } internal class MyCollectionBuilder1 { public static MyCollection1 Create(ReadOnlySpan<long> items) => null; } [CollectionBuilder(typeof(MyCollectionBuilder2), nameof(MyCollectionBuilder2.Create))] public class MyCollection2 { public IEnumerator<long> GetEnumerator() => throw null; } internal class MyCollectionBuilder2 { public static MyCollection2 Create(ReadOnlySpan<long> items) => null; } public class Program { static void Main() { Test1(); Test1(1); Test1(2, 3); Test2(); Test2(1); Test2(2, 3); } public static void Test1(params MyCollection1 a) { } internal static void Test2(params MyCollection2 a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( // (38,30): error CS9224: Method 'MyCollectionBuilder1.Create(ReadOnlySpan<long>)' cannot be less visible than the member with params collection 'Program.Test1(params MyCollection1)'. // public static void Test1(params MyCollection1 a) Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection1 a").WithArguments("MyCollectionBuilder1.Create(System.ReadOnlySpan<long>)", "Program.Test1(params MyCollection1)").WithLocation(38, 30) ); } [Fact] public void CreateMethod_08_Inaccessible() { var src = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public class MyCollection { public IEnumerator<long> GetEnumerator() => throw null; } public class MyCollectionBuilder { protected static MyCollection Create(ReadOnlySpan<long> items) => null; } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( // (19,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // Test(); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test()").WithArguments("Create", "long", "MyCollection").WithLocation(19, 9), // (20,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // Test(1); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(1)").WithArguments("Create", "long", "MyCollection").WithLocation(20, 9), // (21,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // Test(2, 3); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(2, 3)").WithArguments("Create", "long", "MyCollection").WithLocation(21, 9), // (24,29): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // public static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "params MyCollection a").WithArguments("Create", "long", "MyCollection").WithLocation(24, 29) ); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/71854")] public void CreateMethod_09_InDifferentAssembly() { var myCollection_v0Source = """ using System.Collections.Generic; public class MyCollection { public long[] Array; public IEnumerator<long> GetEnumerator() => throw null; } """; var myCollection_v0 = CreateCompilation(myCollection_v0Source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, assemblyName: "Collection"); myCollection_v0.VerifyDiagnostics(); var builderSource = """ using System; public class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<long> items) => new MyCollection() { Array = items.ToArray() }; } """; var builder = CreateCompilation(builderSource, references: [myCollection_v0.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); builder.VerifyDiagnostics(); var myCollectionSource = """ using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection { public long[] Array; public IEnumerator<long> GetEnumerator() => throw null; } """; var myCollection = CreateCompilation(myCollectionSource, references: [builder.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, assemblyName: "Collection"); myCollection.VerifyDiagnostics(); var myCollectionRef = myCollection.EmitToImageReference(); var src = """ class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { if (a.Array.Length == 0) { System.Console.WriteLine(a.Array.Length); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Length, a.Array[0], a.Array[^1]); } } } """; var comp = CreateCompilation(src, references: [myCollectionRef, builder.EmitToImageReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, expectedOutput: ExpectedOutput(@" 0 1: 1 ... 1 2: 2 ... 3 ")).VerifyDiagnostics(); comp = CreateCompilation(src, references: [myCollectionRef], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); // The error improvement is tracked by https://github.com/dotnet/roslyn/issues/71854 comp.VerifyDiagnostics( // (5,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // Test(); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test()").WithArguments("Create", "long", "MyCollection").WithLocation(5, 9), // (6,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // Test(1); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(1)").WithArguments("Create", "long", "MyCollection").WithLocation(6, 9), // (7,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // Test(2, 3); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(2, 3)").WithArguments("Create", "long", "MyCollection").WithLocation(7, 9), // (10,22): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<long>' and return type 'MyCollection'. // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "params MyCollection a").WithArguments("Create", "long", "MyCollection").WithLocation(10, 22) ); } [Fact] public void CreateMethod_10_NoElementType_GetEnumeratorWithParams() { var src = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection { public IEnumerator<long> GetEnumerator(params MyCollection x) => throw null; } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<long> items) => null; public static MyCollection Create(ReadOnlySpan<int> items) => null; } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { foreach (var x in a) { long y = x; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (8,44): error CS0225: The params parameter must have a valid collection type // public IEnumerator<long> GetEnumerator(params MyCollection x) => throw null; Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(8, 44), // (20,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(20, 9), // (21,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(21, 14), // (22,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(22, 9), // (24,22): error CS0225: The params parameter must have a valid collection type // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(24, 22), // (26,27): error CS1579: foreach statement cannot operate on variables of type 'MyCollection' because 'MyCollection' does not contain a public instance or extension definition for 'GetEnumerator' // foreach (var x in a) Diagnostic(ErrorCode.ERR_ForEachMissingMember, "a").WithArguments("MyCollection", "GetEnumerator").WithLocation(26, 27) ); } [Fact] public void CreateMethod_11_NoElementType_MoveNextWithParams() { var src = """ using System; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection { public Enumerator GetEnumerator() => throw null; public class Enumerator { public bool MoveNext(params MyCollection x) => false; public long Current => 0; } } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<long> items) => null; public static MyCollection Create(ReadOnlySpan<int> items) => null; } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { foreach (var x in a) { long y = x; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (11,30): error CS0225: The params parameter must have a valid collection type // public bool MoveNext(params MyCollection x) => false; Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(11, 30), // (25,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(25, 9), // (26,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(26, 14), // (27,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(27, 9), // (29,22): error CS0225: The params parameter must have a valid collection type // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(29, 22), // (31,27): error CS0202: foreach requires that the return type 'MyCollection.Enumerator' of 'MyCollection.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property // foreach (var x in a) Diagnostic(ErrorCode.ERR_BadGetEnumerator, "a").WithArguments("MyCollection.Enumerator", "MyCollection.GetEnumerator()").WithLocation(31, 27) ); } [Fact] public void ImplementsIEnumerableT_01() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long> { public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } static void Test2() { Test([2, 3]); } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, sourceSymbolValidator: static (m) => { VerifyParams(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, symbolValidator: static (m) => { VerifyParamsAndAttribute(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] public void ImplementsIEnumerableT_02() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long> { IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public IEnumerator<string> GetEnumerator() => throw null; public void Add(long l) => throw null; public void Add(string l) => throw null; } class Program { static void Main() { Test("2", 3); Test(["2", 3]); Test("2"); Test(["2"]); Test(3); Test([3]); MyCollection x1 = ["2"]; MyCollection x2 = [3]; } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (19,19): error CS1503: Argument 2: cannot convert from 'int' to 'string' // Test("2", 3); Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("2", "int", "string").WithLocation(19, 19), // (20,14): error CS1729: 'string' does not contain a constructor that takes 0 arguments // Test(["2", 3]); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"[""2"", 3]").WithArguments("string", "0").WithLocation(20, 14), // (20,14): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // Test(["2", 3]); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, @"[""2"", 3]").WithArguments("string", "Add").WithLocation(20, 14), // (20,15): error CS0029: Cannot implicitly convert type 'string' to 'char' // Test(["2", 3]); Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""2""").WithArguments("string", "char").WithLocation(20, 15), // (20,20): error CS0029: Cannot implicitly convert type 'int' to 'char' // Test(["2", 3]); Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "char").WithLocation(20, 20), // (23,14): error CS1503: Argument 1: cannot convert from 'int' to 'string' // Test(3); Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("1", "int", "string").WithLocation(23, 14), // (24,14): error CS1729: 'string' does not contain a constructor that takes 0 arguments // Test([3]); Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[3]").WithArguments("string", "0").WithLocation(24, 14), // (24,14): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // Test([3]); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[3]").WithArguments("string", "Add").WithLocation(24, 14), // (24,15): error CS0029: Cannot implicitly convert type 'int' to 'char' // Test([3]); Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "char").WithLocation(24, 15), // (27,28): error CS0029: Cannot implicitly convert type 'int' to 'string' // MyCollection x2 = [3]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "string").WithLocation(27, 28) ); } [Theory] [CombinatorialData] public void ImplementsIEnumerableT_03_InAttribute(bool asStruct) { var src = @" using System; using System.Collections; using System.Collections.Generic; " + (asStruct ? "struct" : "class") + @" MyCollection : IEnumerable<long> { IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw new InvalidOperationException(); IEnumerator IEnumerable.GetEnumerator() => throw new InvalidOperationException(); public void Add(long l) {} } [Test()] class C1; [Test(1)] class C2; [Test(2, 3)] class C3; class Test : System.Attribute { public Test(params MyCollection a) {} } "; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (14,2): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Test.Test(params MyCollection)' // [Test()] Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test()").WithArguments("a", "Test.Test(params MyCollection)").WithLocation(14, 2), // (17,7): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // [Test(1)] Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(17, 7), // (20,2): error CS1729: 'Test' does not contain a constructor that takes 2 arguments // [Test(2, 3)] Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test(2, 3)").WithArguments("Test", "2").WithLocation(20, 2) ); assertAttributeData("C1"); assertAttributeData("C2"); assertAttributeData("C3"); var tree = comp.SyntaxTrees.Single(); var nodes = tree.GetRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().ToArray(); Assert.Equal(3, nodes.Length); var model = comp.GetSemanticModel(tree); foreach (LiteralExpressionSyntax expression in nodes) { assertTypeInfo(expression); } void assertTypeInfo(LiteralExpressionSyntax expression) { var typeInfo = model.GetTypeInfo(expression); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(expression).IsIdentity); } void assertAttributeData(string name) { var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); Assert.True(attributeData1.HasErrors); Assert.Empty(attributeData1.ConstructorArguments); } } [Fact] public void ImplementsIEnumerableT_04_MissingConstructor() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long> { public MyCollection(int x){} public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (18,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(18, 9), // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14), // (20,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(20, 9), // (23,22): error CS9228: Non-array params collection type must have an applicable constructor that can be called with no arguments. // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsCollectionMissingConstructor, "params MyCollection a").WithLocation(23, 22) ); } [Fact] public void ImplementsIEnumerableT_05_InaccessibleConstructor() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long> { protected MyCollection(){} public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (18,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(18, 9), // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14), // (20,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(20, 9), // (23,22): error CS0122: 'MyCollection.MyCollection()' is inaccessible due to its protection level // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_BadAccess, "params MyCollection a").WithArguments("MyCollection.MyCollection()").WithLocation(23, 22) ); } [Fact] public void ImplementsIEnumerableT_06_LessAccessibleConstructor() { var src = """ using System.Collections; using System.Collections.Generic; public class MyCollection : IEnumerable<long> { internal MyCollection(){} public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => Array.Add(l); } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( // (23,29): error CS9224: Method 'MyCollection.MyCollection()' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. // public static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.MyCollection()", "Program.Test(params MyCollection)").WithLocation(23, 29) ); } [Fact] public void ImplementsIEnumerableT_07_MissingAdd() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long> { public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (15,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(15, 9), // (16,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(16, 14), // (17,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(17, 9), // (20,22): error CS0117: 'MyCollection' does not contain a definition for 'Add' // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_NoSuchMember, "params MyCollection a").WithArguments("MyCollection", "Add").WithLocation(20, 22) ); } [Fact] public void ImplementsIEnumerableT_08_InaccessibleAdd() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long> { public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; protected void Add(long l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (17,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(17, 9), // (18,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(18, 14), // (19,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(19, 9), // (22,22): error CS0122: 'MyCollection.Add(long)' is inaccessible due to its protection level // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_BadAccess, "params MyCollection a").WithArguments("MyCollection.Add(long)").WithLocation(22, 22) ); } [Fact] public void ImplementsIEnumerableT_09_LessAccessibleAdd() { var src = """ using System.Collections; using System.Collections.Generic; public class MyCollection : IEnumerable<long> { public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; internal void Add(long l) => Array.Add(l); } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( // (22,29): error CS9224: Method 'MyCollection.Add(long)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. // public static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.Add(long)", "Program.Test(params MyCollection)").WithLocation(22, 29) ); } [Fact] public void ImplementsIEnumerableT_10_MissingAdd_Dynamic() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public class Enumerator { public bool MoveNext() => throw null; public dynamic Current => null; } } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (20,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(20, 9), // (21,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(21, 14), // (22,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(22, 9), // (25,22): error CS0117: 'MyCollection' does not contain a definition for 'Add' // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_NoSuchMember, "params MyCollection a").WithArguments("MyCollection", "Add").WithLocation(25, 22) ); } [Fact] public void ImplementsIEnumerableT_11_InaccessibleAdd_Dynamic() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; protected void Add(long x){} public class Enumerator { public bool MoveNext() => throw null; public dynamic Current => null; } } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (22,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(22, 9), // (23,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(23, 14), // (24,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(24, 9), // (27,22): error CS0122: 'MyCollection.Add(long)' is inaccessible due to its protection level // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_BadAccess, "params MyCollection a").WithArguments("MyCollection.Add(long)").WithLocation(27, 22) ); } [Fact] public void ImplementsIEnumerableT_12_InaccessibleAdd_Dynamic() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; protected void Add(long x){} protected void Add(int x){} public class Enumerator { public bool MoveNext() => throw null; public dynamic Current => null; } } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (23,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(23, 9), // (24,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(24, 14), // (25,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(25, 9), // (28,22): error CS0122: 'MyCollection.Add(long)' is inaccessible due to its protection level // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_BadAccess, "params MyCollection a").WithArguments("MyCollection.Add(long)").WithLocation(28, 22) ); } [Fact] public void ImplementsIEnumerableT_13_LessAccessibleAdd_Dynamic() { var src = """ using System.Collections; public class MyCollection : IEnumerable { public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; internal void Add(long x){} public class Enumerator { public bool MoveNext() => throw null; public dynamic Current => null; } } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (26,29): error CS9224: Method 'MyCollection.Add(long)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. // public static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.Add(long)", "Program.Test(params MyCollection)").WithLocation(26, 29) ); } [Fact] public void ImplementsIEnumerableT_14_LessAccessibleAdd_Dynamic() { var src = """ using System.Collections; using System.Collections.Generic; public class MyCollection : IEnumerable { public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; protected void Add(long x){} internal void Add(int x){} public class Enumerator { public bool MoveNext() => throw null; public dynamic Current => null; } } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (28,29): error CS9224: Method 'MyCollection.Add(int)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. // public static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.Add(int)", "Program.Test(params MyCollection)").WithLocation(28, 29) ); } [Fact] public void ImplementsIEnumerableT_15_LessAccessibleAdd_Dynamic() { var src = """ using System.Collections; public class MyCollection : IEnumerable { public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; internal void Add(long x){} internal void Add(int x){} public class Enumerator { public bool MoveNext() => throw null; public dynamic Current => null; } } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (27,29): error CS9224: Method 'MyCollection.Add(long)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. // public static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.Add(long)", "Program.Test(params MyCollection)").WithLocation(27, 29) ); } [Fact] public void ImplementsIEnumerableT_16_LessAccessibleAdd_Dynamic() { var src = """ using System.Collections; public class MyCollection : IEnumerable { public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long x){} internal void Add(int x){} public class Enumerator { public bool MoveNext() => throw null; public dynamic Current => null; } } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); } [Fact] public void ImplementsIEnumerableT_17_LessAccessibleAdd_Dynamic() { var src = """ using System.Collections; public class MyCollection : IEnumerable { public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; internal void Add(long x){} public void Add(int x){} public class Enumerator { public bool MoveNext() => throw null; public dynamic Current => null; } } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); } [Fact] public void ImplementsIEnumerableT_18_LessAccessibleAdd_Dynamic() { var src = """ using System.Collections; public class MyCollection : IEnumerable { public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; internal void Add(long x){} public void Add(int x){} internal void Add(byte x){} public class Enumerator { public bool MoveNext() => throw null; public dynamic Current => null; } } public class Program { static void Main() { Test(); Test(1); Test(2, 3); } public static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); } [Fact] public void ImplementsIEnumerableT_19_NoElementType() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long>, IEnumerable<int> { IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => throw null; } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (17,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(17, 9), // (18,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(18, 14), // (19,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(19, 9), // (22,22): error CS0225: The params parameter must have a valid collection type // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(22, 22) ); } [Fact] public void ImplementsIEnumerableT_20_LessAccessibleConstructorAndAdd_NoError_In_LambdaOrLocalFunction() { var src = """ using System.Collections; using System.Collections.Generic; public class MyCollection : IEnumerable<string> { internal MyCollection() { } internal void Add(string s) { } IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } public class Program { public void Test1() { local(); local("a"); void local(params MyCollection collection) { } var x = (params MyCollection collection) => { }; } public void Test2(params MyCollection collection) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( // (25,23): error CS9224: Method 'MyCollection.MyCollection()' cannot be less visible than the member with params collection 'Program.Test2(params MyCollection)'. // public void Test2(params MyCollection collection) Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection collection").WithArguments("MyCollection.MyCollection()", "Program.Test2(params MyCollection)").WithLocation(25, 23), // (25,23): error CS9224: Method 'MyCollection.Add(string)' cannot be less visible than the member with params collection 'Program.Test2(params MyCollection)'. // public void Test2(params MyCollection collection) Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection collection").WithArguments("MyCollection.Add(string)", "Program.Test2(params MyCollection)").WithLocation(25, 23) ); } [Fact] public void ImplementsIEnumerableT_21_AddIsNotAnExtension() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long> { IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } static class Ext { public static void Add(this MyCollection c, long l) {} } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } static void Test2() { Test([2, 3]); } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (24,22): error CS9227: 'MyCollection' does not contain a definition for a suitable instance 'Add' method // static void Test(params MyCollection a) Diagnostic(ErrorCode.ERR_ParamsCollectionExtensionAddMethod, "params MyCollection a").WithArguments("MyCollection").WithLocation(24, 22) ); } [Fact] public void ImplementsIEnumerableT_22_ObsoleteConstructor() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long> { [System.Obsolete()] public MyCollection(){} public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => Array.Add(l); } class Program { static void Main() { #line 100 Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } [System.Obsolete()] static void Test2(params MyCollection a) { Test2(); Test2(1); Test2(2, 3); } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (100,9): warning CS0612: 'MyCollection.MyCollection()' is obsolete // Test(); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "Test()").WithArguments("MyCollection.MyCollection()").WithLocation(100, 9), // (101,9): warning CS0612: 'MyCollection.MyCollection()' is obsolete // Test(1); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "Test(1)").WithArguments("MyCollection.MyCollection()").WithLocation(101, 9), // (102,9): warning CS0612: 'MyCollection.MyCollection()' is obsolete // Test(2, 3); Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "Test(2, 3)").WithArguments("MyCollection.MyCollection()").WithLocation(102, 9), // (105,22): warning CS0612: 'MyCollection.MyCollection()' is obsolete // static void Test(params MyCollection a) Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "params MyCollection a").WithArguments("MyCollection.MyCollection()").WithLocation(105, 22) ); } [Fact] public void ImplementsIEnumerableT_23_ObsoleteAdd() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<long> { public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; [System.Obsolete()] public void Add(long l) => Array.Add(l); } class Program { static void Main() { #line 100 Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } [System.Obsolete()] static void Test2(params MyCollection a) { Test2(); Test2(1); Test2(2, 3); } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (101,14): warning CS1064: The best overloaded Add method 'MyCollection.Add(long)' for the collection initializer element is obsolete. // Test(1); Diagnostic(ErrorCode.WRN_DeprecatedCollectionInitAdd, "1").WithArguments("MyCollection.Add(long)").WithLocation(101, 14), // (102,14): warning CS1064: The best overloaded Add method 'MyCollection.Add(long)' for the collection initializer element is obsolete. // Test(2, 3); Diagnostic(ErrorCode.WRN_DeprecatedCollectionInitAdd, "2").WithArguments("MyCollection.Add(long)").WithLocation(102, 14), // (102,17): warning CS1064: The best overloaded Add method 'MyCollection.Add(long)' for the collection initializer element is obsolete. // Test(2, 3); Diagnostic(ErrorCode.WRN_DeprecatedCollectionInitAdd, "3").WithArguments("MyCollection.Add(long)").WithLocation(102, 17), // (105,22): warning CS1064: The best overloaded Add method 'MyCollection.Add(long)' for the collection initializer element is obsolete. // static void Test(params MyCollection a) Diagnostic(ErrorCode.WRN_DeprecatedCollectionInitAdd, "params MyCollection a").WithArguments("MyCollection.Add(long)").WithLocation(105, 22) ); } [Fact] public void ImplementsIEnumerable_01() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public List<object> Array = new List<object>(); IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } static void Test2() { Test([2, 3]); } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); CompileAndVerify( comp, sourceSymbolValidator: static (m) => { VerifyParams(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, symbolValidator: static (m) => { VerifyParamsAndAttribute(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] public void ImplementsIEnumerable_02() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null; public IEnumerator<string> GetEnumerator() => throw null; public void Add(object l) => throw null; } class Program { static void Main() { Test("2", 3); Test(["2", 3]); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (16,19): error CS1503: Argument 2: cannot convert from 'int' to 'string' // Test("2", 3); Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("2", "int", "string").WithLocation(16, 19), // (17,14): error CS1729: 'string' does not contain a constructor that takes 0 arguments // Test(["2", 3]); Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"[""2"", 3]").WithArguments("string", "0").WithLocation(17, 14), // (17,14): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // Test(["2", 3]); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, @"[""2"", 3]").WithArguments("string", "Add").WithLocation(17, 14), // (17,15): error CS0029: Cannot implicitly convert type 'string' to 'char' // Test(["2", 3]); Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""2""").WithArguments("string", "char").WithLocation(17, 15), // (17,20): error CS0029: Cannot implicitly convert type 'int' to 'char' // Test(["2", 3]); Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "char").WithLocation(17, 20) ); } [Theory] [CombinatorialData] public void ImplementsIEnumerable_03_InAttribute(bool asStruct) { var src = @" using System; using System.Collections; " + (asStruct ? "struct" : "class") + @" MyCollection : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw new InvalidOperationException(); public void Add(object l) {} } [Test()] class C1; [Test(1)] class C2; [Test(2, 3)] class C3; class Test : System.Attribute { public Test(params MyCollection a) {} } "; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (12,2): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Test.Test(params MyCollection)' // [Test()] Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test()").WithArguments("a", "Test.Test(params MyCollection)").WithLocation(12, 2), // (15,7): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' // [Test(1)] Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(15, 7), // (18,2): error CS1729: 'Test' does not contain a constructor that takes 2 arguments // [Test(2, 3)] Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test(2, 3)").WithArguments("Test", "2").WithLocation(18, 2) ); assertAttributeData("C1"); assertAttributeData("C2"); assertAttributeData("C3"); var tree = comp.SyntaxTrees.Single(); var nodes = tree.GetRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().ToArray(); Assert.Equal(3, nodes.Length); var model = comp.GetSemanticModel(tree); foreach (LiteralExpressionSyntax expression in nodes) { assertTypeInfo(expression); } void assertTypeInfo(LiteralExpressionSyntax expression) { var typeInfo = model.GetTypeInfo(expression); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(expression).IsIdentity); } void assertAttributeData(string name) { var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); Assert.True(attributeData1.HasErrors); Assert.Empty(attributeData1.ConstructorArguments); } } [Theory] [InlineData("IEnumerable<long>")] [InlineData("IReadOnlyCollection<long>")] [InlineData("IReadOnlyList<long>")] [InlineData("ICollection<long>")] [InlineData("IList<long>")] public void ArrayInterfaces(string @interface) { var src = """ using System.Collections.Generic; using System.Linq; class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params """ + @interface + """ a) { var array = a.ToArray(); if (array.Length == 0) { System.Console.WriteLine(array.Length); } else { System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[^1]); } } static void Test2() { Test([2, 3]); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, sourceSymbolValidator: static (m) => { VerifyParams(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, symbolValidator: static (m) => { VerifyParamsAndAttribute(m.GlobalNamespace.GetMember<MethodSymbol>("Program.Test").Parameters.Last(), isParamCollection: true); }, expectedOutput: ExpectedOutput(@" 0 1: 1 ... 1 2: 2 ... 3 ")).VerifyDiagnostics(); } [Theory] [InlineData("IEnumerable")] [InlineData("IReadOnlyCollection")] [InlineData("IReadOnlyList")] [InlineData("ICollection")] [InlineData("IList")] public void ArrayInterfaces_InAttribute(string @interface) { var src = @" using System.Collections.Generic; [Test()] class C1; [Test(1)] class C2; [Test(2, 3)] class C3; class Test : System.Attribute { public Test(params " + @interface + @"<long> a) {} } "; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (4,2): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Test.Test(params IReadOnlyList<long>)' // [Test()] Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test()").WithArguments("a", "Test.Test(params System.Collections.Generic." + @interface + "<long>)").WithLocation(4, 2), // (7,7): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IReadOnlyList<long>' // [Test(1)] Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic." + @interface + "<long>").WithLocation(7, 7), // (10,2): error CS1729: 'Test' does not contain a constructor that takes 2 arguments // [Test(2, 3)] Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test(2, 3)").WithArguments("Test", "2").WithLocation(10, 2) ); assertAttributeData("C1"); assertAttributeData("C2"); assertAttributeData("C3"); var tree = comp.SyntaxTrees.Single(); var nodes = tree.GetRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().ToArray(); Assert.Equal(3, nodes.Length); var model = comp.GetSemanticModel(tree); foreach (LiteralExpressionSyntax expression in nodes) { assertTypeInfo(expression); } void assertTypeInfo(LiteralExpressionSyntax expression) { var typeInfo = model.GetTypeInfo(expression); Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.Int32", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(expression).IsIdentity); } void assertAttributeData(string name) { var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); Assert.True(attributeData1.HasErrors); Assert.Empty(attributeData1.ConstructorArguments); } } [Fact] public void IEnumerable() { var src = """ using System.Collections; class Program { static void Test(params IEnumerable a) { Test(new object()); } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (5,22): error CS0225: The params parameter must have a valid collection type // static void Test(params IEnumerable a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(5, 22), // (7,14): error CS1503: Argument 1: cannot convert from 'object' to 'params System.Collections.IEnumerable' // Test(new object()); Diagnostic(ErrorCode.ERR_BadArgType, "new object()").WithArguments("1", "object", "params System.Collections.IEnumerable").WithLocation(7, 14) ); } [Fact] public void WRN_ParamsArrayInLambdaOnly_01() { var src = """ using System.Collections.Generic; class Program { static void Main() { System.Action<IEnumerable<long>> l = (params IEnumerable<long> x) => {}; l(null); } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); CompileAndVerify( comp, symbolValidator: (m) => { MethodSymbol l1 = m.GlobalNamespace.GetMember<MethodSymbol>("Program.<>c.<Main>b__0_0"); AssertEx.Equal("void Program.<>c.<Main>b__0_0(params System.Collections.Generic.IEnumerable<System.Int64> x)", l1.ToTestDisplayString()); VerifyParamsAndAttribute(l1.Parameters.Last(), isParamCollection: true); }).VerifyDiagnostics( // (7,72): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. // System.Action<IEnumerable<long>> l = (params IEnumerable<long> x) => {}; Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(7, 72) ); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Single()); AssertEx.Equal("params System.Collections.Generic.IEnumerable<System.Int64> x", parameter.ToTestDisplayString()); VerifyParams(parameter, isParamCollection: true); var src2 = """ class Program { static void Main() { System.Action<long[]> l = (params long[] x) => {}; l(null); } } """; comp = CreateCompilation(src2, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); comp.VerifyDiagnostics( // (5,36): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' // System.Action<long[]> l = (params long[] x) => {}; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(5, 36), // (5,50): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. // System.Action<long[]> l = (params long[] x) => {}; Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(5, 50) ); comp = CreateCompilation(src2, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); tree = comp.SyntaxTrees.Single(); model = comp.GetSemanticModel(tree); parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Single()); AssertEx.Equal("params System.Int64[] x", parameter.ToTestDisplayString()); VerifyParams(parameter, isParamArray: true); CompileAndVerify( comp, symbolValidator: (m) => { MethodSymbol l1 = m.GlobalNamespace.GetMember<MethodSymbol>("Program.<>c.<Main>b__0_0"); AssertEx.Equal("void Program.<>c.<Main>b__0_0(params System.Int64[] x)", l1.ToTestDisplayString()); VerifyParamsAndAttribute(l1.Parameters.Last(), isParamArray: true); }).VerifyDiagnostics( // (5,50): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. // System.Action<long[]> l = (params long[] x) => {}; Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(5, 50) ); } [Fact] public void WRN_ParamsArrayInLambdaOnly_02() { // public delegate void D1([ParamArrayAttribute] IEnumerable<int> args); // public delegate void D2([ParamCollectionAttribute] int[] args); var il = @" .class public auto ansi sealed D1 extends [mscorlib]System.MulticastDelegate { // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( object 'object', native int 'method' ) runtime managed { } .method public hidebysig newslot virtual instance void Invoke ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> args ) runtime managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) } } .class public auto ansi sealed D2 extends [mscorlib]System.MulticastDelegate { // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( object 'object', native int 'method' ) runtime managed { } .method public hidebysig newslot virtual instance void Invoke ( int32[] args ) runtime managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var src = """ using System.Collections.Generic; class Program { static void Main() { D1 l1 = (params IEnumerable<int> x) => {}; D2 l2 = (params int[] x) => {}; l1 = (IEnumerable<int> x) => {}; l2 = (int[] x) => {}; l1(null); l2(null); } } """; CreateCompilationWithIL(src, il).VerifyEmitDiagnostics(); } [Fact] public void WRN_ParamsArrayInLambdaOnly_03() { var src = """ using System.Collections.Generic; public delegate void D1(params IEnumerable<int> args); public delegate void D2(params int[] args); class Program { static void Main() { D1 l1 = (params IEnumerable<int> x) => {}; D2 l2 = (params int[] x) => {}; l1 = (IEnumerable<int> x) => {}; l2 = (int[] x) => {}; l1(null); l2(null); } } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void ParamCollectionInLocalFunctionOnly() { var src = """ using System.Collections.Generic; class Program { static void Main() { void local (params IEnumerable<long> x) {}; local(1); } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); CompileAndVerify( comp, symbolValidator: (m) => { MethodSymbol l1 = m.GlobalNamespace.GetMember<MethodSymbol>("Program.<Main>g__local|0_0"); AssertEx.Equal("void Program.<Main>g__local|0_0(params System.Collections.Generic.IEnumerable<System.Int64> x)", l1.ToTestDisplayString()); VerifyParamsAndAttribute(l1.Parameters.Last(), isParamCollection: true); }).VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Single()); AssertEx.Equal("params System.Collections.Generic.IEnumerable<System.Int64> x", parameter.ToTestDisplayString()); VerifyParams(parameter, isParamCollection: true); var src2 = """ class Program { static void Main() { void local (params long[] x) {}; local(1); } } """; comp = CreateCompilation(src2, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); comp.VerifyDiagnostics( // (5,21): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' // void local (params long[] x) {}; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params long[] x").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(5, 21) ); comp = CreateCompilation(src2, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); tree = comp.SyntaxTrees.Single(); model = comp.GetSemanticModel(tree); parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Single()); AssertEx.Equal("params System.Int64[] x", parameter.ToTestDisplayString()); VerifyParams(parameter, isParamArray: true); CompileAndVerify( comp, symbolValidator: (m) => { MethodSymbol l1 = m.GlobalNamespace.GetMember<MethodSymbol>("Program.<Main>g__local|0_0"); AssertEx.Equal("void Program.<Main>g__local|0_0(params System.Int64[] x)", l1.ToTestDisplayString()); VerifyParamsAndAttribute(l1.Parameters.Last(), isParamArray: true); }).VerifyDiagnostics(); } [Theory] [InlineData(@"$""Literal{1}""")] [InlineData(@"$""Literal"" + $""{1}""")] public void ConversionInParamsArguments_InterpolatedStringHandler(string expression) { var code = @" using System; using System.Linq; M(" + expression + ", " + expression + @"); void M(params System.ReadOnlySpan<CustomHandler> handlers) { Console.WriteLine(string.Join(Environment.NewLine, handlers.ToArray().Select(h => h.ToString()))); } "; var verifier = CompileAndVerify(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false, includeOneTimeHelpers: false) }, targetFramework: TargetFramework.Net80, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.FailsILVerify with { ILVerifyMessage = InlineArrayTests.InlineArrayAsReadOnlySpanILVerifyMessage + '\n' + InlineArrayTests.InlineArrayElementRefILVerifyMessage } : Verification.Skipped, expectedOutput: ExpectedOutput(@" literal:Literal value:1 alignment:0 format: literal:Literal value:1 alignment:0 format: ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", @" { // Code size 122 (0x7a) .maxstack 5 .locals init (<>y__InlineArray2<CustomHandler> V_0, CustomHandler V_1) IL_0000: ldloca.s V_0 IL_0002: initobj ""<>y__InlineArray2<CustomHandler>"" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: call ""ref CustomHandler <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<CustomHandler>, CustomHandler>(ref <>y__InlineArray2<CustomHandler>, int)"" IL_0010: ldloca.s V_1 IL_0012: ldc.i4.7 IL_0013: ldc.i4.1 IL_0014: call ""CustomHandler..ctor(int, int)"" IL_0019: ldloca.s V_1 IL_001b: ldstr ""Literal"" IL_0020: call ""void CustomHandler.AppendLiteral(string)"" IL_0025: ldloca.s V_1 IL_0027: ldc.i4.1 IL_0028: box ""int"" IL_002d: ldc.i4.0 IL_002e: ldnull IL_002f: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0034: ldloc.1 IL_0035: stobj ""CustomHandler"" IL_003a: ldloca.s V_0 IL_003c: ldc.i4.1 IL_003d: call ""ref CustomHandler <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<CustomHandler>, CustomHandler>(ref <>y__InlineArray2<CustomHandler>, int)"" IL_0042: ldloca.s V_1 IL_0044: ldc.i4.7 IL_0045: ldc.i4.1 IL_0046: call ""CustomHandler..ctor(int, int)"" IL_004b: ldloca.s V_1 IL_004d: ldstr ""Literal"" IL_0052: call ""void CustomHandler.AppendLiteral(string)"" IL_0057: ldloca.s V_1 IL_0059: ldc.i4.1 IL_005a: box ""int"" IL_005f: ldc.i4.0 IL_0060: ldnull IL_0061: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0066: ldloc.1 IL_0067: stobj ""CustomHandler"" IL_006c: ldloca.s V_0 IL_006e: ldc.i4.2 IL_006f: call ""System.ReadOnlySpan<CustomHandler> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray2<CustomHandler>, CustomHandler>(in <>y__InlineArray2<CustomHandler>, int)"" IL_0074: call ""void Program.<<Main>$>g__M|0_0(params System.ReadOnlySpan<CustomHandler>)"" IL_0079: ret } "); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74733")] public void InterpolatedString() { var source = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; C.M("xyz"); [InterpolatedStringHandler] public class C { public C(int literalLength, int formattedCount) { Console.WriteLine($"literal:{literalLength}, formatted:{formattedCount}"); } public void AppendLiteral(params List<string> s) { Console.WriteLine($"append:'{(string.Join(",", s))}'"); } public void AppendFormatted(string s) { Console.WriteLine($"formatted:'{s}'"); } public static void M(string s) { C c = $"abc {s} def"; } } """; CompileAndVerify([source, InterpolatedStringHandlerAttribute], expectedOutput: """ literal:8, formatted:1 append:'abc ' formatted:'xyz' append:' def' """).VerifyDiagnostics(); } [Fact] public void OrderOfEvaluation_01_NamedArguments() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int> { public MyCollection() { System.Console.WriteLine("Create"); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(int l) { System.Console.WriteLine("Add"); } } class Program { static void Main() { Test(b: GetB(), c: GetC(), a: GetA()); } static void Test(int a, int b, params MyCollection c) { } static int GetA() { System.Console.WriteLine("GetA"); return 0; } static int GetB() { System.Console.WriteLine("GetB"); return 0; } static int GetC() { System.Console.WriteLine("GetC"); return 0; } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify( comp, expectedOutput: @" GetB Create GetC Add GetA ").VerifyDiagnostics(); // Note, the collection is created after the lexically previous argument is evaluated, // but before the lexically following argument is evaluated. This differs from params // array case, which is created right before the target method is invoked, after all // arguments are evaluated in their lexical order, which can be observed in a unit-test // Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen.CodeGenTests.NamedParamsOptimizationAndParams002 verifier.VerifyIL("Program.Main", @" { // Code size 36 (0x24) .maxstack 3 .locals init (int V_0, MyCollection V_1) IL_0000: call ""int Program.GetB()"" IL_0005: stloc.0 IL_0006: newobj ""MyCollection..ctor()"" IL_000b: dup IL_000c: call ""int Program.GetC()"" IL_0011: callvirt ""void MyCollection.Add(int)"" IL_0016: stloc.1 IL_0017: call ""int Program.GetA()"" IL_001c: ldloc.0 IL_001d: ldloc.1 IL_001e: call ""void Program.Test(int, int, params MyCollection)"" IL_0023: ret } "); } [Fact] public void OrderOfEvaluation_02_CompoundAssignment() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int> { public MyCollection() { System.Console.WriteLine("Create"); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(int l) { System.Console.WriteLine("Add"); } } class Program { private MyCollection _c; static void Main() { System.Console.WriteLine("---Test1"); Test1(new Program()); System.Console.WriteLine("---Test2"); Test2(new Program()); System.Console.WriteLine("---Test3"); Test3(new Program()); } static void Test1(Program p) { p[GetA()]++; } static void Test2(Program p) { p[GetA(), GetC()]++; } static void Test3(Program p) { p[GetA(), GetB(), GetC()]++; } int this[int a, params MyCollection c] { get { System.Console.WriteLine("Get_this {0}", c is not null); _c = c; return 0; } set { System.Console.WriteLine("Set_this {0}", (object)_c == c); } } static int GetA() { System.Console.WriteLine("GetA"); return 0; } static int GetB() { System.Console.WriteLine("GetB"); return 0; } static int GetC() { System.Console.WriteLine("GetC"); return 0; } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify( comp, expectedOutput: @" ---Test1 GetA Create Get_this True Set_this True ---Test2 GetA Create GetC Add Get_this True Set_this True ---Test3 GetA Create GetB Add GetC Add Get_this True Set_this True ").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", @" { // Code size 33 (0x21) .maxstack 5 .locals init (int V_0, MyCollection V_1, int V_2) IL_0000: ldarg.0 IL_0001: call ""int Program.GetA()"" IL_0006: stloc.0 IL_0007: newobj ""MyCollection..ctor()"" IL_000c: stloc.1 IL_000d: dup IL_000e: ldloc.0 IL_000f: ldloc.1 IL_0010: callvirt ""int Program.this[int, params MyCollection].get"" IL_0015: stloc.2 IL_0016: ldloc.0 IL_0017: ldloc.1 IL_0018: ldloc.2 IL_0019: ldc.i4.1 IL_001a: add IL_001b: callvirt ""void Program.this[int, params MyCollection].set"" IL_0020: ret } "); verifier.VerifyIL("Program.Test2", @" { // Code size 44 (0x2c) .maxstack 5 .locals init (int V_0, MyCollection V_1, int V_2) IL_0000: ldarg.0 IL_0001: call ""int Program.GetA()"" IL_0006: stloc.0 IL_0007: newobj ""MyCollection..ctor()"" IL_000c: dup IL_000d: call ""int Program.GetC()"" IL_0012: callvirt ""void MyCollection.Add(int)"" IL_0017: stloc.1 IL_0018: dup IL_0019: ldloc.0 IL_001a: ldloc.1 IL_001b: callvirt ""int Program.this[int, params MyCollection].get"" IL_0020: stloc.2 IL_0021: ldloc.0 IL_0022: ldloc.1 IL_0023: ldloc.2 IL_0024: ldc.i4.1 IL_0025: add IL_0026: callvirt ""void Program.this[int, params MyCollection].set"" IL_002b: ret } "); verifier.VerifyIL("Program.Test3", @" { // Code size 55 (0x37) .maxstack 5 .locals init (int V_0, MyCollection V_1, int V_2) IL_0000: ldarg.0 IL_0001: call ""int Program.GetA()"" IL_0006: stloc.0 IL_0007: newobj ""MyCollection..ctor()"" IL_000c: dup IL_000d: call ""int Program.GetB()"" IL_0012: callvirt ""void MyCollection.Add(int)"" IL_0017: dup IL_0018: call ""int Program.GetC()"" IL_001d: callvirt ""void MyCollection.Add(int)"" IL_0022: stloc.1 IL_0023: dup IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: callvirt ""int Program.this[int, params MyCollection].get"" IL_002b: stloc.2 IL_002c: ldloc.0 IL_002d: ldloc.1 IL_002e: ldloc.2 IL_002f: ldc.i4.1 IL_0030: add IL_0031: callvirt ""void Program.this[int, params MyCollection].set"" IL_0036: ret } "); } [Fact] public void OrderOfEvaluation_03_ObjectInitializer() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int> { public MyCollection() { System.Console.WriteLine("Create"); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(int l) { System.Console.WriteLine("Add"); } } class C1 { public int F1; public int F2; } class Program { private MyCollection _c; static void Main() { System.Console.WriteLine("---Test1"); Test1(); System.Console.WriteLine("---Test2"); Test2(); System.Console.WriteLine("---Test3"); Test3(); } static void Test1() { _ = new Program() { [GetA()] = { F1 = GetF1(), F2 = GetF2() } }; } static void Test2() { _ = new Program() { [GetA(), GetC()] = { F1 = GetF1(), F2 = GetF2() } }; } static void Test3() { _ = new Program() { [GetA(), GetB(), GetC()] = { F1 = GetF1(), F2 = GetF2() } }; } C1 this[int a, params MyCollection c] { get { System.Console.WriteLine("Get_this {0}", c is not null && (_c is null || (object)_c == c)); _c = c; return new C1(); } set { System.Console.WriteLine("Set_this {0}", (object)_c == c); } } static int GetA() { System.Console.WriteLine("GetA"); return 0; } static int GetB() { System.Console.WriteLine("GetB"); return 0; } static int GetC() { System.Console.WriteLine("GetC"); return 0; } static int GetF1() { System.Console.WriteLine("GetF1"); return 0; } static int GetF2() { System.Console.WriteLine("GetF2"); return 0; } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify( comp, expectedOutput: @" ---Test1 GetA Create Get_this True GetF1 Get_this True GetF2 ---Test2 GetA Create GetC Add Get_this True GetF1 Get_this True GetF2 ---Test3 GetA Create GetB Add GetC Add Get_this True GetF1 Get_this True GetF2 ").VerifyDiagnostics(); // Note, the collection is created once and that same instance is used across multiple invocation of the indexer. // With params arrays, however, only individual elements are cached and each invocation of the indexer is getting // a new instance of an array (with the same values inside though). This can be observed in a unit-test // Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen.ObjectAndCollectionInitializerTests.DictionaryInitializerTestSideeffects001param verifier.VerifyIL("Program.Test1", @" { // Code size 53 (0x35) .maxstack 4 .locals init (int V_0, MyCollection V_1) IL_0000: newobj ""Program..ctor()"" IL_0005: call ""int Program.GetA()"" IL_000a: stloc.0 IL_000b: newobj ""MyCollection..ctor()"" IL_0010: stloc.1 IL_0011: dup IL_0012: ldloc.0 IL_0013: ldloc.1 IL_0014: callvirt ""C1 Program.this[int, params MyCollection].get"" IL_0019: call ""int Program.GetF1()"" IL_001e: stfld ""int C1.F1"" IL_0023: ldloc.0 IL_0024: ldloc.1 IL_0025: callvirt ""C1 Program.this[int, params MyCollection].get"" IL_002a: call ""int Program.GetF2()"" IL_002f: stfld ""int C1.F2"" IL_0034: ret } "); var tree = comp.SyntaxTrees.Single(); var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test1").Single(); VerifyFlowGraph(comp, node, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { CaptureIds: [0] Block[B1] - Block Predecessors: [B0] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new Program ... GetF2() } }') Value: IObjectCreationOperation (Constructor: Program..ctor()) (OperationKind.ObjectCreation, Type: Program) (Syntax: 'new Program ... GetF2() } }') Arguments(0) Initializer: null Next (Regular) Block[B2] Entering: {R2} .locals {R2} { CaptureIds: [1] [2] Block[B2] - Block Predecessors: [B1] Statements (4) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'GetA()') Value: IInvocationOperation (System.Int32 Program.GetA()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'GetA()') Instance Receiver: null Arguments(0) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '[GetA()]') Value: ICollectionExpressionOperation (0 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsImplicit) (Syntax: '[GetA()]') Elements(0) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'F1 = GetF1()') Left: IFieldReferenceOperation: System.Int32 C1.F1 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'F1') Instance Receiver: IPropertyReferenceOperation: C1 Program.this[System.Int32 a, params MyCollection c] { get; set; } (OperationKind.PropertyReference, Type: C1) (Syntax: '[GetA()]') Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: Program, IsImplicit) (Syntax: 'new Program ... GetF2() } }') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: a) (OperationKind.Argument, Type: null) (Syntax: 'GetA()') IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'GetA()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: c) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[GetA()]') IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: MyCollection, IsImplicit) (Syntax: '[GetA()]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInvocationOperation (System.Int32 Program.GetF1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'GetF1()') Instance Receiver: null Arguments(0) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'F2 = GetF2()') Left: IFieldReferenceOperation: System.Int32 C1.F2 (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'F2') Instance Receiver: IPropertyReferenceOperation: C1 Program.this[System.Int32 a, params MyCollection c] { get; set; } (OperationKind.PropertyReference, Type: C1) (Syntax: '[GetA()]') Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: Program, IsImplicit) (Syntax: 'new Program ... GetF2() } }') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: a) (OperationKind.Argument, Type: null) (Syntax: 'GetA()') IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'GetA()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: c) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[GetA()]') IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: MyCollection, IsImplicit) (Syntax: '[GetA()]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: IInvocationOperation (System.Int32 Program.GetF2()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'GetF2()') Instance Receiver: null Arguments(0) Next (Regular) Block[B3] Leaving: {R2} } Block[B3] - Block Predecessors: [B2] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = new Pro ... etF2() } };') Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: Program) (Syntax: '_ = new Pro ... GetF2() } }') Left: IDiscardOperation (Symbol: Program _) (OperationKind.Discard, Type: Program) (Syntax: '_') Right: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: Program, IsImplicit) (Syntax: 'new Program ... GetF2() } }') Next (Regular) Block[B4] Leaving: {R1} } Block[B4] - Exit Predecessors: [B3] Statements (0) """); verifier.VerifyIL("Program.Test2", @" { // Code size 64 (0x40) .maxstack 4 .locals init (int V_0, MyCollection V_1) IL_0000: newobj ""Program..ctor()"" IL_0005: call ""int Program.GetA()"" IL_000a: stloc.0 IL_000b: newobj ""MyCollection..ctor()"" IL_0010: dup IL_0011: call ""int Program.GetC()"" IL_0016: callvirt ""void MyCollection.Add(int)"" IL_001b: stloc.1 IL_001c: dup IL_001d: ldloc.0 IL_001e: ldloc.1 IL_001f: callvirt ""C1 Program.this[int, params MyCollection].get"" IL_0024: call ""int Program.GetF1()"" IL_0029: stfld ""int C1.F1"" IL_002e: ldloc.0 IL_002f: ldloc.1 IL_0030: callvirt ""C1 Program.this[int, params MyCollection].get"" IL_0035: call ""int Program.GetF2()"" IL_003a: stfld ""int C1.F2"" IL_003f: ret } "); verifier.VerifyIL("Program.Test3", @" { // Code size 75 (0x4b) .maxstack 4 .locals init (int V_0, MyCollection V_1) IL_0000: newobj ""Program..ctor()"" IL_0005: call ""int Program.GetA()"" IL_000a: stloc.0 IL_000b: newobj ""MyCollection..ctor()"" IL_0010: dup IL_0011: call ""int Program.GetB()"" IL_0016: callvirt ""void MyCollection.Add(int)"" IL_001b: dup IL_001c: call ""int Program.GetC()"" IL_0021: callvirt ""void MyCollection.Add(int)"" IL_0026: stloc.1 IL_0027: dup IL_0028: ldloc.0 IL_0029: ldloc.1 IL_002a: callvirt ""C1 Program.this[int, params MyCollection].get"" IL_002f: call ""int Program.GetF1()"" IL_0034: stfld ""int C1.F1"" IL_0039: ldloc.0 IL_003a: ldloc.1 IL_003b: callvirt ""C1 Program.this[int, params MyCollection].get"" IL_0040: call ""int Program.GetF2()"" IL_0045: stfld ""int C1.F2"" IL_004a: ret } "); } [Fact] public void OrderOfEvaluation_04_ObjectInitializer() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int> { public MyCollection() { System.Console.WriteLine("Create"); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(int l) { System.Console.WriteLine("Add"); } } class C1 { } class Program { private MyCollection _c; static void Main() { System.Console.WriteLine("---Test1"); Test1(); System.Console.WriteLine("---Test2"); Test2(); System.Console.WriteLine("---Test3"); Test3(); } static void Test1() { _ = new Program() { [GetA()] = { } }; } static void Test2() { _ = new Program() { [GetA(), GetC()] = { } }; } static void Test3() { _ = new Program() { [GetA(), GetB(), GetC()] = { } }; } C1 this[int a, params MyCollection c] { get { System.Console.WriteLine("Get_this {0}", c is not null && (_c is null || (object)_c == c)); _c = c; return new C1(); } set { System.Console.WriteLine("Set_this {0}", (object)_c == c); } } static int GetA() { System.Console.WriteLine("GetA"); return 0; } static int GetB() { System.Console.WriteLine("GetB"); return 0; } static int GetC() { System.Console.WriteLine("GetC"); return 0; } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify( comp, expectedOutput: @" ---Test1 GetA Create ---Test2 GetA Create GetC Add ---Test3 GetA Create GetB Add GetC Add ").VerifyDiagnostics(); // Note, the collection is created even though the getter is never invoked verifier.VerifyIL("Program.Test1", @" { // Code size 19 (0x13) .maxstack 1 IL_0000: newobj ""Program..ctor()"" IL_0005: pop IL_0006: call ""int Program.GetA()"" IL_000b: pop IL_000c: newobj ""MyCollection..ctor()"" IL_0011: pop IL_0012: ret } "); var tree = comp.SyntaxTrees.Single(); var node = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Where(m => m.Identifier.ValueText == "Test1").Single(); VerifyFlowGraph(comp, node, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { CaptureIds: [0] Block[B1] - Block Predecessors: [B0] Statements (4) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new Program ... ()] = { } }') Value: IObjectCreationOperation (Constructor: Program..ctor()) (OperationKind.ObjectCreation, Type: Program) (Syntax: 'new Program ... ()] = { } }') Arguments(0) Initializer: null IInvocationOperation (System.Int32 Program.GetA()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'GetA()') Instance Receiver: null Arguments(0) ICollectionExpressionOperation (0 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsImplicit) (Syntax: '[GetA()]') Elements(0) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = new Pro ... )] = { } };') Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: Program) (Syntax: '_ = new Pro ... ()] = { } }') Left: IDiscardOperation (Symbol: Program _) (OperationKind.Discard, Type: Program) (Syntax: '_') Right: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: Program, IsImplicit) (Syntax: 'new Program ... ()] = { } }') Next (Regular) Block[B2] Leaving: {R1} } Block[B2] - Exit Predecessors: [B1] Statements (0) """); verifier.VerifyIL("Program.Test2", @" { // Code size 28 (0x1c) .maxstack 2 IL_0000: newobj ""Program..ctor()"" IL_0005: pop IL_0006: call ""int Program.GetA()"" IL_000b: pop IL_000c: newobj ""MyCollection..ctor()"" IL_0011: call ""int Program.GetC()"" IL_0016: callvirt ""void MyCollection.Add(int)"" IL_001b: ret } "); verifier.VerifyIL("Program.Test3", @" { // Code size 39 (0x27) .maxstack 3 IL_0000: newobj ""Program..ctor()"" IL_0005: pop IL_0006: call ""int Program.GetA()"" IL_000b: pop IL_000c: newobj ""MyCollection..ctor()"" IL_0011: dup IL_0012: call ""int Program.GetB()"" IL_0017: callvirt ""void MyCollection.Add(int)"" IL_001c: call ""int Program.GetC()"" IL_0021: callvirt ""void MyCollection.Add(int)"" IL_0026: ret } "); } [Fact] public void LanguageVersion_01_Declaration() { var src = @" class Program { static void Test1(params System.ReadOnlySpan<long> a) {} static void Test2(params long[] a) {} void Test() { Test1(1); Test2(2); } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics(); comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular13); comp.VerifyDiagnostics(); comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (4,23): error CS9202: Feature 'params collections' is not available in C# 12.0. Please use language version 13.0 or greater. // static void Test1(params System.ReadOnlySpan<long> a) {} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "params System.ReadOnlySpan<long> a").WithArguments("params collections", "13.0").WithLocation(4, 23) ); } [Fact] public void LanguageVersion_02_CallSite() { var src1 = @" public class Params { static public void Test1(params System.ReadOnlySpan<long> a) {} static public void Test2(params long[] a) {} } "; var src2 = @" class Program { void Test() { Params.Test1(1); Params.Test2(2); Params.Test1(); Params.Test2(); Params.Test1([1]); Params.Test2([2]); } } "; var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); verify(comp1.ToMetadataReference()); verify(comp1.EmitToImageReference()); void verify(MetadataReference comp1Ref) { var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); comp2.VerifyDiagnostics(); comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular13); comp2.VerifyDiagnostics(); comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp2.VerifyDiagnostics( // (6,22): error CS1503: Argument 1: cannot convert from 'int' to 'params System.ReadOnlySpan<long>' // Params.Test1(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.ReadOnlySpan<long>").WithLocation(6, 22), // (9,16): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Params.Test1(params ReadOnlySpan<long>)' // Params.Test1(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test1").WithArguments("a", "Params.Test1(params System.ReadOnlySpan<long>)").WithLocation(9, 16) ); } } [Fact] public void LanguageVersion_03_DelegateNaturalType() { var src1 = @" public class Params { static public void Test1(params System.ReadOnlySpan<long> a) {} static public void Test2(params long[] a) {} } "; var src2 = @" class Program { void Test() { var x1 = Params.Test1; var x2 = Params.Test2; x1(1); x2(2); x1(); x2(); x1([1]); x2([2]); } } "; var comp = CreateCompilation(src2 + src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics(); comp = CreateCompilation(src2 + src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular13); comp.VerifyDiagnostics(); comp = CreateCompilation(src2 + src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (22,30): error CS9202: Feature 'params collections' is not available in C# 12.0. Please use language version 13.0 or greater. // static public void Test1(params System.ReadOnlySpan<long> a) {} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "params System.ReadOnlySpan<long> a").WithArguments("params collections", "13.0").WithLocation(22, 30) ); var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); verify(comp1.ToMetadataReference()); verify(comp1.EmitToImageReference()); void verify(MetadataReference comp1Ref) { var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); comp2.VerifyDiagnostics(); comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular13); comp2.VerifyDiagnostics(); comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp2.VerifyDiagnostics( // (9,12): error CS1503: Argument 1: cannot convert from 'int' to 'scoped System.ReadOnlySpan<long>' // x1(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "scoped System.ReadOnlySpan<long>").WithLocation(9, 12), // (12,9): error CS7036: There is no argument given that corresponds to the required parameter 'arg' of '<anonymous delegate>' // x1(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x1").WithArguments("arg", "<anonymous delegate>").WithLocation(12, 9) ); } } [Fact] public void LanguageVersion_04_DelegateNaturalType() { var src = @" class Program { void Test() { var x1 = (params System.ReadOnlySpan<long> a) => {}; var x2 = (params long[] a) => {}; x1(1); x2(2); x1(); x2(); x1([1]); x2([2]); M1(x1); M1(x2); M1((params System.ReadOnlySpan<long> b) => {}); M1((params long[] b) => {}); } static void M1<T>(T t) {} } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics(); comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular13); comp.VerifyDiagnostics(); comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (6,19): error CS9202: Feature 'params collections' is not available in C# 12.0. Please use language version 13.0 or greater. // var x1 = (params System.ReadOnlySpan<long> a) => {}; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "params System.ReadOnlySpan<long> a").WithArguments("params collections", "13.0").WithLocation(6, 19), // (21,13): error CS9202: Feature 'params collections' is not available in C# 12.0. Please use language version 13.0 or greater. // M1((params System.ReadOnlySpan<long> b) => {}); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "params System.ReadOnlySpan<long> b").WithArguments("params collections", "13.0").WithLocation(21, 13) ); } [Fact] public void LanguageVersion_05_DelegateNaturalType() { var src1 = @" public class Params { static public void Test1(params System.Collections.Generic.IEnumerable<long> a) {} static public void Test2(params long[] a) {} } "; var src2 = @" class Program { void Test1() { var a = Params.Test1; M1(a); // See DelegateNaturalType_03 unit-test for an observable effect that 'params' modifier has for this invocation. M1(Params.Test1); } static void M1<T>(T t) {} void Test2() { var b = Params.Test2; M1(b); M1(Params.Test2); } } "; var comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics(); comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular13); comp.VerifyDiagnostics(); comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (23,30): error CS9202: Feature 'params collections' is not available in C# 12.0. Please use language version 13.0 or greater. // static public void Test1(params System.Collections.Generic.IEnumerable<long> a) {} Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "params System.Collections.Generic.IEnumerable<long> a").WithArguments("params collections", "13.0").WithLocation(23, 30) ); var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); verify(comp1.ToMetadataReference()); verify(comp1.EmitToImageReference()); void verify(MetadataReference comp1Ref) { var comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); var verifier = CompileAndVerify(comp2, symbolValidator: checkParamsInDelegate1).VerifyDiagnostics(); void checkParamsInDelegate1(ModuleSymbol m) { Assert.True(m.GlobalNamespace.GetTypeMember("<>f__AnonymousDelegate0").DelegateInvokeMethod.Parameters.Last().IsParams); } var expectedIL = @" { // Code size 65 (0x41) .maxstack 2 IL_0000: ldsfld ""<anonymous delegate> Program.<>O.<0>__Test1"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop IL_0009: ldnull IL_000a: ldftn ""void Params.Test1(params System.Collections.Generic.IEnumerable<long>)"" IL_0010: newobj ""<>f__AnonymousDelegate0..ctor(object, System.IntPtr)"" IL_0015: dup IL_0016: stsfld ""<anonymous delegate> Program.<>O.<0>__Test1"" IL_001b: call ""void Program.M1<<anonymous delegate>>(<anonymous delegate>)"" IL_0020: ldsfld ""<anonymous delegate> Program.<>O.<0>__Test1"" IL_0025: dup IL_0026: brtrue.s IL_003b IL_0028: pop IL_0029: ldnull IL_002a: ldftn ""void Params.Test1(params System.Collections.Generic.IEnumerable<long>)"" IL_0030: newobj ""<>f__AnonymousDelegate0..ctor(object, System.IntPtr)"" IL_0035: dup IL_0036: stsfld ""<anonymous delegate> Program.<>O.<0>__Test1"" IL_003b: call ""void Program.M1<<anonymous delegate>>(<anonymous delegate>)"" IL_0040: ret } "; verifier.VerifyIL("Program.Test1", expectedIL); comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular13); verifier = CompileAndVerify(comp2, symbolValidator: checkParamsInDelegate1).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", expectedIL); comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); verifier = CompileAndVerify(comp2, symbolValidator: checkParamsInDelegate1).VerifyDiagnostics(); // Note, we are using System.Action. which doesn't have params verifier.VerifyIL("Program.Test1", @" { // Code size 65 (0x41) .maxstack 2 IL_0000: ldsfld ""System.Action<System.Collections.Generic.IEnumerable<long>> Program.<>O.<0>__Test1"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop IL_0009: ldnull IL_000a: ldftn ""void Params.Test1(params System.Collections.Generic.IEnumerable<long>)"" IL_0010: newobj ""System.Action<System.Collections.Generic.IEnumerable<long>>..ctor(object, System.IntPtr)"" IL_0015: dup IL_0016: stsfld ""System.Action<System.Collections.Generic.IEnumerable<long>> Program.<>O.<0>__Test1"" IL_001b: call ""void Program.M1<System.Action<System.Collections.Generic.IEnumerable<long>>>(System.Action<System.Collections.Generic.IEnumerable<long>>)"" IL_0020: ldsfld ""System.Action<System.Collections.Generic.IEnumerable<long>> Program.<>O.<0>__Test1"" IL_0025: dup IL_0026: brtrue.s IL_003b IL_0028: pop IL_0029: ldnull IL_002a: ldftn ""void Params.Test1(params System.Collections.Generic.IEnumerable<long>)"" IL_0030: newobj ""System.Action<System.Collections.Generic.IEnumerable<long>>..ctor(object, System.IntPtr)"" IL_0035: dup IL_0036: stsfld ""System.Action<System.Collections.Generic.IEnumerable<long>> Program.<>O.<0>__Test1"" IL_003b: call ""void Program.M1<System.Action<System.Collections.Generic.IEnumerable<long>>>(System.Action<System.Collections.Generic.IEnumerable<long>>)"" IL_0040: ret } "); } } [Fact] public void LanguageVersion_06_LambdaForDelegateWithParams() { var src1 = @" public class Params { static public void Test1(D1 d) {} static public void Test2(D2 d) {} } public delegate void D1(params System.Collections.Generic.IEnumerable<long> a); public delegate void D2(params long[] a); "; var src2 = @" class Program1 { void Test1() { Params.Test1(e1 => { }); } } class Program2 { void Test2() { Params.Test2(e2 => { }); } } "; var comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics(); comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular13); comp.VerifyDiagnostics(); comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (23,25): error CS9202: Feature 'params collections' is not available in C# 12.0. Please use language version 13.0 or greater. // public delegate void D1(params System.Collections.Generic.IEnumerable<long> a); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "params System.Collections.Generic.IEnumerable<long> a").WithArguments("params collections", "13.0").WithLocation(23, 25) ); var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); verify(comp1.ToMetadataReference()); verify(comp1.EmitToImageReference()); void verify(MetadataReference comp1Ref) { var comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); comp2.VerifyDiagnostics(); comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular13); comp2.VerifyDiagnostics(); comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular12); var tree = comp2.SyntaxTrees.Single(); var model = comp2.GetSemanticModel(tree); var parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().First()); AssertEx.Equal("System.Collections.Generic.IEnumerable<System.Int64> e1", parameter.ToTestDisplayString()); Assert.False(parameter.IsParams); Assert.False(parameter.IsParamsArray); Assert.False(parameter.IsParamsCollection); parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Skip(1).First()); AssertEx.Equal("System.Int64[] e2", parameter.ToTestDisplayString()); Assert.False(parameter.IsParams); Assert.False(parameter.IsParamsArray); Assert.False(parameter.IsParamsCollection); CompileAndVerify(comp2, symbolValidator: (m) => { var lambda = m.GlobalNamespace.GetMember<MethodSymbol>("Program1.<>c.<Test1>b__0_0"); ParameterSymbol parameter = lambda.Parameters.Single(); VerifyParamsAndAttribute(parameter); Assert.Equal("System.Collections.Generic.IEnumerable<System.Int64> e1", parameter.ToTestDisplayString()); lambda = m.GlobalNamespace.GetMember<MethodSymbol>("Program2.<>c.<Test2>b__0_0"); parameter = lambda.Parameters.Single(); VerifyParamsAndAttribute(parameter); Assert.Equal("System.Int64[] e2", parameter.ToTestDisplayString()); } ).VerifyDiagnostics(); // No language version diagnostics as expected. The 'params' modifier doesn't even make it to symbol and metadata. } } [Fact] [WorkItem("https://github.com/dotnet/csharplang/issues/8061")] public void LanguageVersion_07_CallSite() { var src1 = @" public class Params { static public void Test1(params System.ReadOnlySpan<long> a) { System.Console.Write(""span""); } static public void Test1(params long[] a) { System.Console.Write(""array""); } } "; var src2 = @" class Program { static void Main() { Params.Test1(1); } } "; var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); verify(comp1.ToMetadataReference()); verify(comp1.EmitToImageReference()); void verify(MetadataReference comp1Ref) { var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); CompileAndVerify(comp2, expectedOutput: ExpectedOutput("span"), verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); CompileAndVerify(comp2, expectedOutput: ExpectedOutput("span"), verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); CompileAndVerify(comp2, expectedOutput: ExpectedOutput("array"), verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); } } [Fact] public void DelegateNaturalType_01() { var src1 = @" public class Params { static public void Test1(params System.ReadOnlySpan<long> a) { System.Console.WriteLine(a.Length); } static public void Test2(params long[] a) { System.Console.WriteLine(a.Length); } } "; var src2 = @" class Program { static void Main() { var x1 = Params.Test1; var x2 = Params.Test2; x1(1); x2(2); x1(); x2(); } } "; var comp = CreateCompilation(src1 + src2, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); verify(comp, attributeIsEmbedded: true); var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); verify(comp2, attributeIsEmbedded: true); var comp3 = CreateCompilation(src1 + ParamCollectionAttributeSource, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); var comp4 = CreateCompilation(src2, references: [comp3.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); verify(comp4, attributeIsEmbedded: false); var comp5 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseModule); comp5.VerifyDiagnostics(); void verify(CSharpCompilation comp, bool attributeIsEmbedded) { // We want to test attribute embedding Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, symbolValidator: (m) => { MethodSymbol delegateInvokeMethod1 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod; AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.ReadOnlySpan<System.Int64> arg)", delegateInvokeMethod1.ToTestDisplayString()); VerifyParamsAndAttribute(delegateInvokeMethod1.Parameters.Last(), isParamCollection: true); MethodSymbol delegateInvokeMethod2 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1`1").DelegateInvokeMethod; AssertEx.Equal("void <>f__AnonymousDelegate1<T1>.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); if (attributeIsEmbedded) { Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); } else { Assert.Empty(m.GlobalNamespace.GetMembers("System")); } }, expectedOutput: ExpectedOutput(@" 1 1 0 0 ")).VerifyDiagnostics(); } } [Fact] public void DelegateNaturalType_02() { var src = @" class Program { static void Main() { var x1 = (params System.ReadOnlySpan<long> a) => System.Console.WriteLine(a.Length); var x2 = (params long[] a) => System.Console.WriteLine(a.Length); x1(1); x2(2); x1(); x2(); } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); verify(comp, attributeIsEmbedded: true); var comp1 = CreateCompilation(ParamCollectionAttributeSource, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); var comp2 = CreateCompilation(src, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); verify(comp2, attributeIsEmbedded: false); void verify(CSharpCompilation comp, bool attributeIsEmbedded) { // We want to test attribute embedding Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, symbolValidator: (m) => { MethodSymbol delegateInvokeMethod1 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod; AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.ReadOnlySpan<System.Int64> arg)", delegateInvokeMethod1.ToTestDisplayString()); VerifyParamsAndAttribute(delegateInvokeMethod1.Parameters.Last(), isParamCollection: true); MethodSymbol delegateInvokeMethod2 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1`1").DelegateInvokeMethod; AssertEx.Equal("void <>f__AnonymousDelegate1<T1>.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); MethodSymbol l1 = m.GlobalNamespace.GetMember<MethodSymbol>("Program.<>c.<Main>b__0_0"); AssertEx.Equal("void Program.<>c.<Main>b__0_0(params System.ReadOnlySpan<System.Int64> a)", l1.ToTestDisplayString()); VerifyParamsAndAttribute(l1.Parameters.Last(), isParamCollection: true); MethodSymbol l2 = m.GlobalNamespace.GetMember<MethodSymbol>("Program.<>c.<Main>b__0_1"); AssertEx.Equal("void Program.<>c.<Main>b__0_1(params System.Int64[] a)", l2.ToTestDisplayString()); VerifyParamsAndAttribute(l2.Parameters.Last(), isParamArray: true); if (attributeIsEmbedded) { Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); } else { Assert.Empty(m.GlobalNamespace.GetMembers("System")); } }, expectedOutput: ExpectedOutput(@" 1 1 0 0 ")).VerifyDiagnostics(); } } [Fact] public void DelegateNaturalType_03() { var src = @" class Program { static public void Test1(System.Collections.Generic.IEnumerable<long> a) { System.Console.WriteLine("" {0}"", a is not null); } static public void Test2(params System.Collections.Generic.IEnumerable<long> a) { System.Console.WriteLine("" {0}"", a is not null); } static public void Test3(params System.Collections.Generic.List<long> a) { System.Console.WriteLine("" {0}"", a is not null); } static public void Test4(params long[] a) { System.Console.WriteLine("" {0}"", a is not null); } static void Main() { DoTest1(); DoTest21(); DoTest22(); DoTest3(); DoTest4(); } static void DoTest1() { var a1 = Test1; M(a1); } static void DoTest21() { var a2 = Test2; M(a2)(); } static void DoTest22() { var a2 = Test2; M(a2)(); } static void DoTest3() { var a3 = Test3; M(a3)(); } static void DoTest4() { var a4 = Test4; M(a4)(); } static T M<T>(T t) { System.Console.WriteLine(typeof(T)); return t; } static void M(System.Action<System.Collections.Generic.IEnumerable<long>> t) => System.Console.WriteLine(""Action""); static void M(System.Action<System.Collections.Generic.List<long>> t) => System.Console.WriteLine(""Action""); } "; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify( comp, symbolValidator: (m) => { AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable<System.Int64> arg)", m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod.ToTestDisplayString()); AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params System.Collections.Generic.List<System.Int64> arg)", m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1").DelegateInvokeMethod.ToTestDisplayString()); AssertEx.Equal("void <>f__AnonymousDelegate2<T1>.Invoke(params T1[] arg)", m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate2`1").DelegateInvokeMethod.ToTestDisplayString()); }, expectedOutput: ExpectedOutput(@" Action <>f__AnonymousDelegate0 True <>f__AnonymousDelegate0 True <>f__AnonymousDelegate1 True <>f__AnonymousDelegate2`1[System.Int64] True ")).VerifyDiagnostics(); } [Fact] public void DelegateNaturalType_04() { var src = @" class Program { static void Main() { var x1 = Test1; var x2 = Test2; x1(1); x2(2); x1(); x2(); static void Test1(params System.ReadOnlySpan<long> a) { System.Console.WriteLine(a.Length); } static void Test2(params long[] a) { System.Console.WriteLine(a.Length); } } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); verify(comp, attributeIsEmbedded: true); var comp1 = CreateCompilation(ParamCollectionAttributeSource, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); var comp2 = CreateCompilation(src, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); verify(comp2, attributeIsEmbedded: false); void verify(CSharpCompilation comp, bool attributeIsEmbedded) { // We want to test attribute embedding Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, symbolValidator: (m) => { MethodSymbol delegateInvokeMethod1 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod; AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.ReadOnlySpan<System.Int64> arg)", delegateInvokeMethod1.ToTestDisplayString()); VerifyParamsAndAttribute(delegateInvokeMethod1.Parameters.Last(), isParamCollection: true); MethodSymbol delegateInvokeMethod2 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1`1").DelegateInvokeMethod; AssertEx.Equal("void <>f__AnonymousDelegate1<T1>.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); MethodSymbol l1 = m.GlobalNamespace.GetMember<MethodSymbol>("Program.<Main>g__Test1|0_0"); AssertEx.Equal("void Program.<Main>g__Test1|0_0(params System.ReadOnlySpan<System.Int64> a)", l1.ToTestDisplayString()); VerifyParamsAndAttribute(l1.Parameters.Last(), isParamCollection: true); MethodSymbol l2 = m.GlobalNamespace.GetMember<MethodSymbol>("Program.<Main>g__Test2|0_1"); AssertEx.Equal("void Program.<Main>g__Test2|0_1(params System.Int64[] a)", l2.ToTestDisplayString()); VerifyParamsAndAttribute(l2.Parameters.Last(), isParamArray: true); if (attributeIsEmbedded) { Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); } else { Assert.Empty(m.GlobalNamespace.GetMembers("System")); } }, expectedOutput: ExpectedOutput(@" 1 1 0 0 ")).VerifyDiagnostics(); } } [Fact] public void DelegateNaturalType_05() { var src1 = @" using System.Collections.Generic; public class Params { static public void Test1(params IEnumerable<long> a) {} } "; var src2 = @" class Program { static void Main() { var x1 = Params.Test1; x1(1); x1(); } } "; var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); comp2.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); comp2.VerifyEmitDiagnostics(); } [Fact] public void DelegateNaturalType_06() { var src1 = @" public class Params { static public void Test1(params long[] a) {} } "; var src2 = @" class Program { static void Main() { var x1 = Params.Test1; x1(1); x1(); } } "; var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); comp2.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); comp2.VerifyDiagnostics( // (6,18): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' // var x1 = Params.Test1; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "Params.Test1").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(6, 18) ); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/73242")] public void DelegateNaturalType_07() { var src = @" class Helper<T> where T : System.Collections.Generic.List<long>, new() { static public void Test3(params T a) { System.Console.WriteLine("" {0}"", a is not null); } } class Program { static void Main() { DoTest3<System.Collections.Generic.List<long>>(); } static void DoTest3<T>() where T : System.Collections.Generic.List<long>, new() { var a3 = Helper<T>.Test3; M(a3)(); } static T M<T>(T t) { System.Console.WriteLine(typeof(T)); return t; } } "; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify( comp, symbolValidator: (m) => { var delegateType = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0`1"); MethodSymbol delegateInvokeMethod = delegateType.DelegateInvokeMethod; AssertEx.Equal("void <>f__AnonymousDelegate0<T1>.Invoke(params T1 arg)", delegateInvokeMethod.ToTestDisplayString()); // Strictly speaking it is a violation of language rules to decorate this parameter with ParamCollectionAttribute // because it lacks constraints that would make it a valid 'params' type // However, going through the trouble of porting constraints and differentiating/merging anonymous delegates based on them // is probably not worth the trouble for this edge scenario. // The types involved might be inaccessible on assembly level, etc. VerifyParamsAndAttribute(delegateInvokeMethod.Parameters[0], isParamArray: false, isParamCollection: true); Assert.False(delegateType.TypeParameters[0].HasConstructorConstraint); Assert.Empty(delegateType.TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics); }, expectedOutput: ExpectedOutput(@" <>f__AnonymousDelegate0`1[System.Collections.Generic.List`1[System.Int64]] True ")).VerifyDiagnostics(); } [Fact] public void BetterNess_01_ElementType() { var src = @" class Program { static void Main() { int x = 1; Test(x); byte y = 1; Test(y); } static void Test(params System.Span<long> a) { System.Console.WriteLine(""long""); } static void Test(params System.Span<int> a) { System.Console.WriteLine(""int""); } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify( comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput(@" int int")).VerifyDiagnostics(); } [Fact] public void BetterNess_02_ElementType() { var src = @" class Program { static void Main() { Test(new C2()); Test(new C3()); } static void Test(params System.Span<C1> a) { System.Console.WriteLine(""C1""); } static void Test(params System.Span<C2> a) { System.Console.WriteLine(""C2""); } } class C1 {} class C2 : C1 {} class C3 : C2 {} "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify( comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput(@" C2 C2")).VerifyDiagnostics(); } [Fact] public void BetterNess_03_ElementType() { var src = @" using System.Collections; using System.Collections.Generic; class C1 : IEnumerable<char> { public static void M1(params C1 x) { } public static void M1(params ushort[] x) { } void Test() { M1('a', 'b'); M2('a', 'b'); } public static void M2(params ushort[] x) { } IEnumerator<char> IEnumerable<char>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( // (7,27): error CS0117: 'C1' does not contain a definition for 'Add' // public static void M1(params C1 x) Diagnostic(ErrorCode.ERR_NoSuchMember, "params C1 x").WithArguments("C1", "Add").WithLocation(7, 27) ); } [Theory] [InlineData("System.Span<T>", "T[]", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.IEnumerable<T>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.IReadOnlyCollection<T>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.IReadOnlyList<T>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.ICollection<T>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.IList<T>", "System.Span<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "T[]", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.IEnumerable<T>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.IReadOnlyCollection<T>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.IReadOnlyList<T>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.ICollection<T>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.IList<T>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.HashSet<T>", null)] // rule requires array or array interface // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("System.Span<T>", "System.ReadOnlySpan<object>", "System.Span<System.Int32>")] // cannot convert from object to int [InlineData("RefStructCollection<T>", "T[]", null, new[] { CollectionExpressionTests.example_RefStructCollection })] // rule requires span // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("RefStructCollection<T>", "RefStructCollection<object>", "RefStructCollection<System.Int32>", new[] { CollectionExpressionTests.example_RefStructCollection })] // rule requires span // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("RefStructCollection<int>", "GenericClassCollection<object>", "RefStructCollection<System.Int32>", new[] { CollectionExpressionTests.example_RefStructCollection, CollectionExpressionTests.example_GenericClassCollection })] // rule requires span // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("RefStructCollection<object>", "GenericClassCollection<int>", "GenericClassCollection<System.Int32>", new[] { CollectionExpressionTests.example_RefStructCollection, CollectionExpressionTests.example_GenericClassCollection })] // cannot convert object to int // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("RefStructCollection<int>", "NonGenericClassCollection", "RefStructCollection<System.Int32>", new[] { CollectionExpressionTests.example_RefStructCollection, CollectionExpressionTests.example_NonGenericClassCollection })] // rule requires span [InlineData("GenericClassCollection<T>", "T[]", null, new[] { CollectionExpressionTests.example_GenericClassCollection })] // rule requires span [InlineData("NonGenericClassCollection", "object[]", null, new[] { CollectionExpressionTests.example_NonGenericClassCollection })] // rule requires span [InlineData("System.ReadOnlySpan<T>", "object[]", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "long[]", "System.ReadOnlySpan<System.Int32>")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) [InlineData("System.ReadOnlySpan<T>", "short[]", "System.ReadOnlySpan<System.Int32>")] // cannot convert int to short // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case [InlineData("System.ReadOnlySpan<long>", "T[]", "System.Int32[]")] // cannot convert long to int // Ambiguous for inline collection expression, but 'long' is a better conversion target than 'object' in params case [InlineData("System.ReadOnlySpan<object>", "long[]", "System.Int64[]")] // cannot convert object to long [InlineData("System.ReadOnlySpan<long>", "object[]", "System.ReadOnlySpan<System.Int64>")] [InlineData("System.ReadOnlySpan<long>", "string[]", "System.ReadOnlySpan<System.Int64>")] [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<string>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Span<T>", "System.ReadOnlySpan<System.Int32>")] // Inline collection expression picks "System.ReadOnlySpan<System.Int32>", but that params candidate is worse because it is generic [InlineData("System.ReadOnlySpan<T>", "System.Span<int>", "System.Span<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Span<object>", "System.ReadOnlySpan<System.Int32>")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) [InlineData("System.ReadOnlySpan<T>", "System.Span<short>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.ReadOnlySpan<int>", "System.ReadOnlySpan<System.Int32>")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("System.ReadOnlySpan<T>", "System.ReadOnlySpan<object>", "System.ReadOnlySpan<System.Int32>")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case [InlineData("System.ReadOnlySpan<T>", "System.ReadOnlySpan<long>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.Span<T>", "System.Span<int>", "System.Span<System.Int32>")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("System.Span<T>", "System.Span<object>", "System.Span<System.Int32>")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) [InlineData("System.Span<T>", "System.Span<short>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Span<string>", "System.Span<System.Int32>")] [InlineData("T[]", "int[]", "System.Int32[]")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("T[]", "object[]", "System.Int32[]")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case [InlineData("T[]", "int?[]", "System.Int32[]")] [InlineData("System.Collections.Generic.ICollection<T>", "System.Collections.Generic.ICollection<int>", "System.Collections.Generic.ICollection<System.Int32>")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("System.Collections.Generic.ICollection<T>", "System.Collections.Generic.ICollection<object>", "System.Collections.Generic.ICollection<System.Int32>")] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) [InlineData("System.Collections.Generic.ICollection<T>", "System.Collections.Generic.ICollection<short>", "System.Collections.Generic.ICollection<System.Int32>")] [InlineData("System.Collections.Generic.ICollection<T>", "System.Collections.Generic.IReadOnlyCollection<T>", null)] [InlineData("MyCollectionA<T>", "MyCollectionB<T>", "MyCollectionB<System.Int32>", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] // Inline collection expression picks "MyCollectionB<System.Int32>", but that params candidate is worse because it is generic [InlineData("MyCollectionA<int>", "MyCollectionB<T>", "MyCollectionA<System.Int32>", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case [InlineData("MyCollectionA<T>", "MyCollectionB<long>", "MyCollectionA<System.Int32>", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("MyCollectionA<T>", "MyCollectionB<object>", "MyCollectionA<System.Int32>", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case [InlineData("MyCollectionB<T>", "MyCollectionB<long>", "MyCollectionB<System.Int32>", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] [InlineData("RefStructConvertibleFromArray<T>", "T[]", "System.Int32[]", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] [InlineData("RefStructConvertibleFromArray<T>", "int[]", "System.Int32[]", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("RefStructConvertibleFromArray<object>", "T[]", "System.Int32[]", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case [InlineData("RefStructConvertibleFromArray<T>", "object[]", "RefStructConvertibleFromArray<System.Int32>", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] public void BetterConversionFromExpression_01A(string type1, string type2, string expectedType, string[] additionalSources = null) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; class Program { {{generateMethod("F1", type1)}} {{generateMethod("F1", type2)}} {{generateMethod("F2", type2)}} {{generateMethod("F2", type1)}} static void Main() { var x = F1(1, 2, 3); Console.WriteLine(x.GetTypeName()); var y = F2(4, 5); Console.WriteLine(y.GetTypeName()); } } """; var comp = CreateCompilation( getSources(source, additionalSources), targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); if (expectedType is { }) { CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" {expectedType} {expectedType} """)); } else { comp.VerifyEmitDiagnostics( // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(ReadOnlySpan<long>)' and 'Program.F1(ReadOnlySpan<object>)' // var x = F1(1, 2, 3); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(ReadOnlySpan<object>)' and 'Program.F2(ReadOnlySpan<long>)' // var y = F2(4, 5); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); } static string getTypeParameters(string type) => type.Contains("T[]") || type.Contains("<T>") ? "<T>" : ""; static string generateMethod(string methodName, string parameterType) => $"static Type {methodName}{getTypeParameters(parameterType)}(params {parameterType} value) => typeof({parameterType});"; static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}{getTypeParameters(parameterType)}(params {parameterType})"; static string[] getSources(string source, string[] additionalSources) { var builder = ArrayBuilder<string>.GetInstance(); builder.Add(source); builder.Add(CollectionExpressionTests.s_collectionExtensions); if (additionalSources is { }) builder.AddRange(additionalSources); return builder.ToArrayAndFree(); } } [Theory] [InlineData("System.ReadOnlySpan<int>", "System.Span<int>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Span<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Span<int?>", null)] [InlineData("System.ReadOnlySpan<object>", "System.Span<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<int?>", "System.Span<int>", null)] // cannot convert int? to int [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<int?>", null)] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<int?>", null)] [InlineData("System.Span<int>", "System.Span<object>", null)] [InlineData("System.Span<int>", "System.Span<int?>", null)] [InlineData("System.Span<object>", "System.Span<int?>", null)] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<long>", null)] [InlineData("System.Span<int>", "int?[]", null)] [InlineData("System.Span<int>", "System.Collections.Generic.IEnumerable<int?>", null)] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyCollection<int?>", null)] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyList<int?>", null)] [InlineData("System.Span<int>", "System.Collections.Generic.ICollection<int?>", null)] [InlineData("System.Span<int>", "System.Collections.Generic.IList<int?>", null)] [InlineData("System.Span<int?>", "int[]", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IEnumerable<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyCollection<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyList<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.ICollection<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IList<int>", null)] // cannot convert int? to int [InlineData("System.ReadOnlySpan<int>", "object[]", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IEnumerable<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyCollection<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyList<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.ICollection<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IList<object>", null)] [InlineData("System.ReadOnlySpan<object>", "int[]", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IEnumerable<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyCollection<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyList<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.ICollection<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IList<int>", null)] // cannot convert object to int [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<System.Int32>")] [InlineData("int[]", "object[]", null)] // rule requires span [InlineData("int[]", "System.Collections.Generic.IReadOnlyList<object>", null)] // rule requires span [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.List<byte>", null)] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<long>", null)] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<ulong>", null)] [InlineData("System.Collections.Generic.List<short>", "System.Collections.Generic.List<long>", null)] [InlineData("System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<byte>", null)] [InlineData("int[]", "System.Collections.Generic.List<byte>", null)] [InlineData("System.Collections.Generic.HashSet<short>", "System.Span<long>", null)] [InlineData("System.Collections.Generic.HashSet<short>", "System.ReadOnlySpan<long>", null)] [InlineData("System.Collections.Generic.HashSet<long>", "System.Span<short>", null)] [InlineData("System.Collections.Generic.HashSet<long>", "System.ReadOnlySpan<short>", null)] public void BetterConversionFromExpression_01B_Empty(string type1, string type2, string expectedType) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; class Program { {{generateMethod("F1", type1)}} {{generateMethod("F1", type2)}} {{generateMethod("F2", type2)}} {{generateMethod("F2", type1)}} static void Main() { var a = F1(); Console.WriteLine(a.GetTypeName()); var b = F2(); Console.WriteLine(b.GetTypeName()); } } """; var comp = CreateCompilation( new[] { source, CollectionExpressionTests.s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); if (expectedType is { }) { CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" {expectedType} {expectedType} """)); } else { comp.VerifyEmitDiagnostics( // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' // var a = F1(); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' // var b = F2(); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); } static string generateMethod(string methodName, string parameterType) => $"static Type {methodName}(params {parameterType} value) => typeof({parameterType});"; static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}(params {parameterType})"; } [Theory] [InlineData("System.ReadOnlySpan<int>", "System.Span<int>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Span<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Span<int?>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Span<int>", "System.Span<System.Int32>")] [InlineData("System.ReadOnlySpan<int?>", "System.Span<int>", "System.Span<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<int?>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<int?>", "System.ReadOnlySpan<System.Nullable<System.Int32>>")] [InlineData("System.Span<int>", "System.Span<object>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Span<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<object>", "System.Span<int?>", "System.Span<System.Nullable<System.Int32>>")] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<long>", "System.ReadOnlySpan<System.Int64>")] [InlineData("System.Span<int>", "int?[]", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IEnumerable<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyCollection<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyList<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.ICollection<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IList<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int?>", "int[]", "System.Int32[]")] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.IEnumerable<System.Int32>")] [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyCollection<int>", "System.Collections.Generic.IReadOnlyCollection<System.Int32>")] [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyList<int>", "System.Collections.Generic.IReadOnlyList<System.Int32>")] [InlineData("System.Span<int?>", "System.Collections.Generic.ICollection<int>", "System.Collections.Generic.ICollection<System.Int32>")] [InlineData("System.Span<int?>", "System.Collections.Generic.IList<int>", "System.Collections.Generic.IList<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "object[]", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IEnumerable<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyCollection<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyList<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.ICollection<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IList<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "int[]", "System.Int32[]")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.IEnumerable<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyCollection<int>", "System.Collections.Generic.IReadOnlyCollection<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyList<int>", "System.Collections.Generic.IReadOnlyList<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.ICollection<int>", "System.Collections.Generic.ICollection<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IList<int>", "System.Collections.Generic.IList<System.Int32>")] [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<System.Int32>")] [InlineData("int[]", "object[]", "System.Int32[]")] [InlineData("int[]", "System.Collections.Generic.IReadOnlyList<object>", "System.Int32[]")] [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.List<byte>", "System.Collections.Generic.List<System.Int32>")] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<long>", null)] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<ulong>", "System.Collections.Generic.List<System.Nullable<System.Int32>>")] [InlineData("System.Collections.Generic.List<short>", "System.Collections.Generic.List<long>", "System.Collections.Generic.List<System.Int16>")] [InlineData("System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<byte>", "System.Collections.Generic.IEnumerable<System.Int32>")] [InlineData("int[]", "System.Collections.Generic.List<byte>", "System.Int32[]")] [InlineData("System.Collections.Generic.HashSet<short>", "System.Span<long>", "System.Collections.Generic.HashSet<System.Int16>")] [InlineData("System.Collections.Generic.HashSet<short>", "System.ReadOnlySpan<long>", "System.Collections.Generic.HashSet<System.Int16>")] [InlineData("System.Collections.Generic.HashSet<long>", "System.Span<short>", "System.Span<System.Int16>")] [InlineData("System.Collections.Generic.HashSet<long>", "System.ReadOnlySpan<short>", "System.ReadOnlySpan<System.Int16>")] public void BetterConversionFromExpression_01B_NotEmpty(string type1, string type2, string expectedType) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; class Program { {{generateMethod("F1", type1)}} {{generateMethod("F1", type2)}} {{generateMethod("F2", type2)}} {{generateMethod("F2", type1)}} static void Main() { var c = F1(1, 2, 3); Console.WriteLine(c.GetTypeName()); var d = F2(4, 5); Console.WriteLine(d.GetTypeName()); } } """; var comp = CreateCompilation( new[] { source, CollectionExpressionTests.s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); if (expectedType is { }) { CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" {expectedType} {expectedType} """)); } else { comp.VerifyEmitDiagnostics( // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' // var c = F1(1, 2, 3); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' // var d = F2(4, 5); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); } static string generateMethod(string methodName, string parameterType) => $"static Type {methodName}(params {parameterType} value) => typeof({parameterType});"; static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}(params {parameterType})"; } [Theory, CombinatorialData] public void BetterConversionFromExpression_01C( [CombinatorialValues( "System.ReadOnlySpan<string>", "System.Span<string>", "string[]", "System.Collections.Generic.IEnumerable<string>", "System.Collections.Generic.IReadOnlyList<string>", "System.Collections.Generic.IReadOnlyCollection<string>", "System.Collections.Generic.IList<string>", "System.Collections.Generic.ICollection<string>" )] string stringType, [CombinatorialValues( "System.ReadOnlySpan<CustomHandler>", "System.Span<CustomHandler>", "CustomHandler[]", "System.Collections.Generic.IEnumerable<CustomHandler>", "System.Collections.Generic.IReadOnlyList<CustomHandler>", "System.Collections.Generic.IReadOnlyCollection<CustomHandler>", "System.Collections.Generic.IList<CustomHandler>", "System.Collections.Generic.ICollection<CustomHandler>")] string interpolatedType) // This is a clone of a unit-test from CollectionExpressionTests.cs { var testMethods = $$""" partial class Program { {{generateMethod("F1", stringType)}} {{generateMethod("F1", interpolatedType)}} {{generateMethod("F2", interpolatedType)}} {{generateMethod("F2", stringType)}} } """; var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false, includeOneTimeHelpers: false); string source1 = $$""" var a = F1(); var b = F2(); var c = F1($"{1}", $"2", $"{3}"); var d = F2($"{1}", $"2", $"{3}"); """; var comp = CreateCompilation( [source1, testMethods, customHandler, CollectionExpressionTests.s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); string f1InterpolatedSig = generateMethodSignature("F1", interpolatedType); string f1StringSig = generateMethodSignature("F1", stringType); string f2InterpolatedSig = generateMethodSignature("F2", interpolatedType); string f2StringSig = generateMethodSignature("F2", stringType); comp.VerifyDiagnostics( // (1,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(IReadOnlyList<string>)' and 'Program.F1(IReadOnlyCollection<CustomHandler>)' // var a = F1(); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(f1StringSig, f1InterpolatedSig).WithLocation(1, 9), // (2,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(IReadOnlyCollection<CustomHandler>)' and 'Program.F2(IReadOnlyList<string>)' // var b = F2(); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(f2InterpolatedSig, f2StringSig).WithLocation(2, 9), // (3,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(string[])' and 'Program.F1(CustomHandler[])' // var c = F1($"{1}", $"2", $"{3}"); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(f1StringSig, f1InterpolatedSig).WithLocation(3, 9), // (4,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(CustomHandler[])' and 'Program.F2(string[])' // var d = F2($"{1}", $"2", $"{3}"); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(f2InterpolatedSig, f2StringSig).WithLocation(4, 9) ); var source2 = $$""" using System; var c = F1($"{1}", $"{2}", $"{3}"); Console.WriteLine(c.GetTypeName()); var d = F2($"{1}", $"{2}", $"{3}"); Console.WriteLine(d.GetTypeName()); var e = F1([$"1", $"2", $"3"]); Console.WriteLine(e.GetTypeName()); var f = F2([$"1", $"2", $"3"]); Console.WriteLine(f.GetTypeName()); """; comp = CreateCompilation( [source2, testMethods, customHandler, CollectionExpressionTests.s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); string outputStringType = stringType.Replace("string", "System.String"); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" {interpolatedType} {interpolatedType} {outputStringType} {outputStringType} """)); static string generateMethod(string methodName, string parameterType) => $"static System.Type {methodName}(params {parameterType} value) => typeof({parameterType});"; static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}(params {parameterType})"; } [Fact] public void BetterConversionFromExpression_02() // This is a clone of a unit-test from CollectionExpressionTests.cs { string sourceA = """ using System; using static System.Console; partial class Program { static void Generic<T>(params Span<T> value) { WriteLine("Span<T>"); } static void Generic<T>(params T[] value) { WriteLine("T[]"); } static void Identical(params Span<string> value) { WriteLine("Span<string>"); } static void Identical(params string[] value) { WriteLine("string[]"); } static void SpanDerived(params Span<string> value) { WriteLine("Span<string>"); } static void SpanDerived(params object[] value) { WriteLine("object[]"); } static void ArrayDerived(params Span<object> value) { WriteLine("Span<object>"); } static void ArrayDerived(params string[] value) { WriteLine("string[]"); } } """; string sourceB1 = """ partial class Program { static void Main() { Generic(new[] { string.Empty }); // string[] Identical(new[] { string.Empty }); // string[] ArrayDerived(new[] { string.Empty }); // string[] Generic(string.Empty); // Span<string> Identical(string.Empty); // Span<string> SpanDerived(string.Empty); // Span<string> // Ambiguous for inline collection expression, but 'string' is a better conversion target than 'object' in params case ArrayDerived(string.Empty); } } """; var comp = CreateCompilation( new[] { sourceA, sourceB1 }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput(""" T[] string[] string[] Span<T> Span<string> Span<string> string[] """)); } [WorkItem("https://github.com/dotnet/roslyn/issues/69634")] [Fact] public void BetterConversionFromExpression_03() // This is a clone of a unit-test from CollectionExpressionTests.cs { string sourceA = """ using System; using static System.Console; partial class Program { static void Unrelated(params Span<int> value) { WriteLine("Span<int>"); } static void Unrelated(params string[] value) { WriteLine("string[]"); } } """; string sourceB1 = """ partial class Program { static void Main() { Unrelated(new[] { 1 }); // Span<int> Unrelated(new[] { string.Empty }); // string[] Unrelated(2); // Span<int> Unrelated(string.Empty); // string[] } } """; var comp = CreateCompilation( new[] { sourceA, sourceB1 }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput(""" Span<int> string[] Span<int> string[] """)); string sourceB2 = """ partial class Program { static void Main() { Unrelated(new[] { default }); // error Unrelated(default); // ambiguous } } """; comp = CreateCompilation( new[] { sourceA, sourceB2 }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 1.cs(5,19): error CS0826: No best type found for implicitly-typed array // Unrelated(new[] { default }); // error Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { default }").WithLocation(5, 19), // 1.cs(5,19): error CS1503: Argument 1: cannot convert from '?[]' to 'int' // Unrelated(new[] { default }); // error Diagnostic(ErrorCode.ERR_BadArgType, "new[] { default }").WithArguments("1", "?[]", "int").WithLocation(5, 19), // 1.cs(6,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Unrelated(params Span<int>)' and 'Program.Unrelated(params string[])' // Unrelated(default); // ambiguous Diagnostic(ErrorCode.ERR_AmbigCall, "Unrelated").WithArguments("Program.Unrelated(params System.Span<int>)", "Program.Unrelated(params string[])").WithLocation(6, 9) ); } [Fact] public void BetterConversionFromExpression_04() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = """ using System; class Program { static void F1(int[] x, params int[] y) { throw null; } static void F1(Span<object> x, params ReadOnlySpan<int> y) { x.Report(); y.Report(); } static void F2(object x, params string[] y) { throw null; } static void F2(string x, params Span<object> y) { y.Report(); } static void Main() { F1([1], 2); F2("3", "4"); } } """; // Both calls are ambiguous for collection expressions due to different betterness among arguments. CreateCompilation( new[] { source, CollectionExpressionTests.s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80).VerifyDiagnostics( // 0.cs(11,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object, params string[])' and 'Program.F2(string, params Span<object>)' // F2("3", "4"); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(object, params string[])", "Program.F2(string, params System.Span<object>)").WithLocation(11, 9) ); } [Fact] public void BetterConversionFromExpression_05() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = """ using System; class Program { static void F1(Span<int> x, params int[] y) { x.Report(); y.Report(); } static void F1(int[] x, params ReadOnlySpan<int> y) { throw null; } static void F2(string x, params string[] y) { y.Report(); } static void F2(object x, params Span<string> y) { throw null; } static void Main() { F1([1], 2); F2("3", "4"); } } """; // Both calls are ambiguous for inline collection expressions, due to better-ness in different directions among arguments. // For params case, there is no difference in the target type for the second argument CompileAndVerify( new[] { source, CollectionExpressionTests.s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: ExpectedOutput("[1], [2], [4], ")); } // Two ref struct collection types, with an implicit conversion from one to the other. [Fact] public void BetterConversionFromExpression_06() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create1))] ref struct MyCollection1<T> { private readonly List<T> _list; public MyCollection1(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); public static implicit operator MyCollection2<T>(MyCollection1<T> c) => new(c._list); } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create2))] ref struct MyCollection2<T> { private readonly List<T> _list; public MyCollection2(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); } static class MyCollectionBuilder { public static MyCollection1<T> Create1<T>(scoped ReadOnlySpan<T> items) { return new MyCollection1<T>(new List<T>(items.ToArray())); } public static MyCollection2<T> Create2<T>(scoped ReadOnlySpan<T> items) { return new MyCollection2<T>(new List<T>(items.ToArray())); } } class Program { static void F1<T>(params MyCollection1<T> c) { Console.WriteLine("MyCollection1<T>"); } static void F1<T>(params MyCollection2<T> c) { Console.WriteLine("MyCollection2<T>"); } static void F2(params MyCollection2<object> c) { Console.WriteLine("MyCollection2<object>"); } static void F2(params MyCollection1<object> c) { Console.WriteLine("MyCollection1<object>"); } static void Main() { F1(1, 2, 3); F2(4, null); F1((MyCollection1<object>)[6]); F1((MyCollection2<int>)[7]); F2((MyCollection2<object>)[8]); } } """; CompileAndVerify( source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: ExpectedOutput(""" MyCollection1<T> MyCollection1<object> MyCollection1<T> MyCollection2<T> MyCollection2<object> """)); } [Fact] public void BetterConversionFromExpression_07() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = """ using System; class Program { static void F1(params ReadOnlySpan<int> value) { Console.WriteLine("int"); } static void F1(params ReadOnlySpan<object> value) { } static void F2(params Span<string> value) { Console.WriteLine("string"); } static void F2(params Span<object> value) { } static void Main() { F1(1, 2, 3); F2("a", "b"); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); // Ambiguity in case of inline collection expression CompileAndVerify( comp, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.FailsILVerify with { ILVerifyMessage = InlineArrayTests.InlineArrayAsSpanILVerifyMessage + '\n' + InlineArrayTests.InlineArrayElementRefILVerifyMessage } : Verification.Skipped, expectedOutput: ExpectedOutput(@" int string ")).VerifyDiagnostics(); } [Fact] public void BetterConversionFromExpression_08A() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = """ class Program { static void F1(params int[] value) { System.Console.WriteLine("int"); } static void F1(params object[] value) { } static void Main() { F1(1, 2, 3); } } """; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); // No behavior change (param arrays). Ambiguity in case of inline collection expression CompileAndVerify( comp, expectedOutput: @"int").VerifyDiagnostics(); } [Fact] public void BetterConversionFromExpression_08B() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = """ using System; class Program { static void F2(params string[] value) { Console.WriteLine("string[]"); } static void F2(params object[] value) { Console.WriteLine("object[]"); } static void Main() { F2("a", "b"); } } """; CompileAndVerify(source, expectedOutput: "string[]"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73857")] public void BetterConversionFromExpression_08C() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = """ using System; class Program { static void F2(params ReadOnlySpan<string> value) { Console.WriteLine("ReadOnlySpan<string>"); } static void F2(params ReadOnlySpan<object> value) { Console.WriteLine("ReadOnlySpan<object>"); } static void Main() { F2("a", "b"); } } """; CompileAndVerify(source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: ExpectedOutput("ReadOnlySpan<string>")); } [Fact] public void BetterConversionFromExpression_09() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = """ using System; using System.Collections.Generic; class Program { static void F2(params List<int> value) { Console.WriteLine("List<int>"); } static void F2(params List<byte> value) { Console.WriteLine("List<byte>"); } static void Main() { F2(1, (byte)2); } } """; CreateCompilation(source).VerifyDiagnostics( // (9,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(List<int>)' and 'Program.F2(List<byte>)' // F2([1, (byte)2]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(params System.Collections.Generic.List<int>)", "Program.F2(params System.Collections.Generic.List<byte>)").WithLocation(9, 9)); } [Fact] public void BetterConversionFromExpression_10() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = """ using System; using System.Collections.Generic; class Program { static void F2(params List<int> value) { Console.WriteLine("List<int>"); } static void F2(params List<byte> value) { Console.WriteLine("List<byte>"); } static void Main() { F2((byte)1, (byte)2); } } """; CompileAndVerify(source, parseOptions: TestOptions.Regular13, expectedOutput: "List<byte>"); } [Theory] [InlineData("System.FormattableString")] [InlineData("System.IFormattable")] public void BetterConversionFromExpression_11(string formatType) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; class Program { static void F2(params ReadOnlySpan<string> value) { Console.WriteLine("ReadOnlySpan<string>"); } static void F2(params ReadOnlySpan<{{formatType}}> value) { Console.WriteLine("ReadOnlySpan<{{formatType}}>"); } static void Main() { F2($"{1}"); } } """; CompileAndVerify(source, parseOptions: TestOptions.Regular13, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: ExpectedOutput("ReadOnlySpan<string>")); } [Theory] [InlineData("System.ReadOnlySpan<char>")] [InlineData("System.Span<char>")] public void BetterConversionFromExpression_String_01(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; using static System.Console; class Program { static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(params string value) { WriteLine("F1(string)"); } static void F2(params string value) { WriteLine("F2(string)"); } static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1(); F2(); F1('a', 'b', 'c'); F2('1', '2', '3'); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (7,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F1(params string value) { WriteLine("F1(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(7, 20), // (8,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F2(params string value) { WriteLine("F2(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 20) ); } [Theory] [InlineData("System.ReadOnlySpan<int>")] [InlineData("System.Span<int>")] [InlineData("System.ReadOnlySpan<object>")] [InlineData("System.Span<object>")] public void BetterConversionFromExpression_String_02_Empty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; using static System.Console; class Program { static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(params string value) { WriteLine("F1(string)"); } static void F2(params string value) { WriteLine("F2(string)"); } static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1(); F2(); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (7,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F1(params string value) { WriteLine("F1(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(7, 20), // (8,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F2(params string value) { WriteLine("F2(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 20) ); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var f1 = tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(id => id.Identifier.ValueText == "F1").Single(); Assert.NotEqual(SpecialType.System_String, model.GetSymbolInfo(f1).Symbol.GetParameters().Single().Type.SpecialType); var f2 = tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(id => id.Identifier.ValueText == "F2").Single(); Assert.NotEqual(SpecialType.System_String, model.GetSymbolInfo(f2).Symbol.GetParameters().Single().Type.SpecialType); } [Theory] [InlineData("System.ReadOnlySpan<int>")] [InlineData("System.Span<int>")] [InlineData("System.ReadOnlySpan<object>")] [InlineData("System.Span<object>")] public void BetterConversionFromExpression_String_02_NotEmpty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; using static System.Console; class Program { static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(params string value) { WriteLine("F1(string)"); } static void F2(params string value) { WriteLine("F2(string)"); } static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1('a', 'b', 'c'); F2('1', '2', '3'); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (7,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F1(params string value) { WriteLine("F1(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(7, 20), // (8,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F2(params string value) { WriteLine("F2(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 20) ); } [Theory] [InlineData("System.ReadOnlySpan<byte>")] [InlineData("System.Span<byte>")] public void BetterConversionFromExpression_String_03(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; using static System.Console; class Program { static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(params string value) { WriteLine("F1(string)"); } static void F2(params string value) { WriteLine("F2(string)"); } static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1(); F2(); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (7,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F1(params string value) { WriteLine("F1(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(7, 20), // (8,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F2(params string value) { WriteLine("F2(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 20) ); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var f1 = tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(id => id.Identifier.ValueText == "F1").Single(); Assert.NotEqual(SpecialType.System_String, model.GetSymbolInfo(f1).Symbol.GetParameters().Single().Type.SpecialType); var f2 = tree.GetRoot().DescendantNodes().OfType<IdentifierNameSyntax>().Where(id => id.Identifier.ValueText == "F2").Single(); Assert.NotEqual(SpecialType.System_String, model.GetSymbolInfo(f2).Symbol.GetParameters().Single().Type.SpecialType); } [Theory] [InlineData("System.ReadOnlySpan<MyChar>")] [InlineData("System.Span<MyChar>")] public void BetterConversionFromExpression_String_04_Empty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; using static System.Console; class MyChar { private readonly int _i; public MyChar(int i) { _i = i; } public static implicit operator MyChar(int i) => new MyChar(i); public static implicit operator char(MyChar c) => (char)c._i; } class Program { static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(params string value) { WriteLine("F1(string)"); } static void F2(params string value) { WriteLine("F2(string)"); } static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1(); F2(); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (15,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F1(params string value) { WriteLine("F1(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(15, 20), // (16,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F2(params string value) { WriteLine("F2(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(16, 20) ); } [Theory] [InlineData("System.ReadOnlySpan<MyChar>")] [InlineData("System.Span<MyChar>")] public void BetterConversionFromExpression_String_04_NotEmpty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using static System.Console; class MyChar { private readonly int _i; public MyChar(int i) { _i = i; } public static implicit operator MyChar(int i) => new MyChar(i); public static implicit operator char(MyChar c) => (char)c._i; } class Program { static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(params string value) { WriteLine("F1(string)"); } static void F2(params string value) { WriteLine("F2(string)"); } static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1('a', 'b', 'c'); F2('1', '2', '3'); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (14,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F1(params string value) { WriteLine("F1(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(14, 20), // (15,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F2(params string value) { WriteLine("F2(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(15, 20) ); } [Fact] public void BetterConversionFromExpression_String_05() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" using System; using System.Collections.Generic; using static System.Console; class Program { static void F(params IEnumerable<char> value) { WriteLine("F(IEnumerable<char>)"); } static void F(params string value) { WriteLine("F(string)"); } static void Main() { F(); F('a'); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,19): error CS1729: 'string' does not contain a constructor that takes 0 arguments // static void F(params string value) { WriteLine("F(string)"); } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 19) ); } [Fact] public void BetterOverload_01_BetterParameterPassing() { // the better parameter-passing choice (https://github.com/dotnet/csharpstandard/blob/draft-v9/standard/expressions.md#12644-better-parameter-passing-mode) // should come before collection better-ness, but after argument conversion better-ness. // Expected output below matches legacy behavior of param arrays. var src = """ class Program { static void Main() { Test(1); Test(1, new C2()); } static void Test(in int x, params C2[] y) { System.Console.Write("In"); } static void Test(int x, params C1[] y) { System.Console.Write("Val"); } } class C1 {} class C2 : C1 {} """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput(@"ValIn")).VerifyDiagnostics(); } [Fact] public void BetterOverload_02_NotSameCollectionElements() { var src = """ class Program { static void Main() { Test(x: 1, y: 2); } static void Test(int x, params System.ReadOnlySpan<int> y) { System.Console.Write("ReadOnlySpan"); } static void Test(int y, params System.Span<int> x) { System.Console.Write("Span"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (5,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Test(int, params ReadOnlySpan<int>)' and 'Program.Test(int, params Span<int>)' // Test(x: 1, y: 2); Diagnostic(ErrorCode.ERR_AmbigCall, "Test").WithArguments("Program.Test(int, params System.ReadOnlySpan<int>)", "Program.Test(int, params System.Span<int>)").WithLocation(5, 9) ); } [Fact] public void BetterOverload_03_NotSameCollectionElements() { var src = """ class Program { static void Main() { Test(x: 1, y: 2); } static void Test(long x, params System.ReadOnlySpan<int> y) { System.Console.Write("ReadOnlySpan"); } static void Test(int y, params long[] x) { System.Console.Write("Span"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (5,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Test(long, params ReadOnlySpan<int>)' and 'Program.Test(int, params long[])' // Test(x: 1, y: 2); Diagnostic(ErrorCode.ERR_AmbigCall, "Test").WithArguments("Program.Test(long, params System.ReadOnlySpan<int>)", "Program.Test(int, params long[])").WithLocation(5, 9) ); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/72696")] public void BetterOverload_04_Ambiguity() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } static class ExtensionsA { public static void Add<T>(this MyCollection<T> collection, params string[] args) { } } static class ExtensionsB { public static void Add<T>(this MyCollection<T> collection, params string[] args) { } } class Program { static void Main() { var x = new MyCollection<object>(); x.Add(""); var y = new MyCollection<object> { "" }; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (25,11): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add<T>(MyCollection<T>, params string[])' and 'ExtensionsB.Add<T>(MyCollection<T>, params string[])' // x.Add(""); Diagnostic(ErrorCode.ERR_AmbigCall, "Add").WithArguments("ExtensionsA.Add<T>(MyCollection<T>, params string[])", "ExtensionsB.Add<T>(MyCollection<T>, params string[])").WithLocation(25, 11), // (27,44): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add<T>(MyCollection<T>, params string[])' and 'ExtensionsB.Add<T>(MyCollection<T>, params string[])' // var y = new MyCollection<object> { "" }; Diagnostic(ErrorCode.ERR_AmbigCall, @"""""").WithArguments("ExtensionsA.Add<T>(MyCollection<T>, params string[])", "ExtensionsB.Add<T>(MyCollection<T>, params string[])").WithLocation(27, 44) ); } [Fact] public void GenericInference() { var src = """ using System.Collections.Generic; class Program { static void Main() { long l = 1; int i = 2; byte b = 3; Test(i, b, l); } static void Test<T>(params IEnumerable<T> b) { System.Console.Write(typeof(T)); } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); CompileAndVerify( comp, expectedOutput: ExpectedOutput(@"System.Int64")).VerifyDiagnostics(); } [Fact] public void DynamicInvocation_OrdinaryMethod_01() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test(d); Test(d, 1); Test(d, 2, 3); Test(2, d, 3); Test(2, 3, d); Test2(d, d); Test2(d, 1); Test2(d, 2, 3); Test2(2, d, 3); Test2(2, 3, d); Test2<int>(d); Test2<int>(d, d); Test2<int>(d, 1); Test2<int>(d, 2, 3); Test2<int>(2, d, 3); Test2<int>(2, 3, d); } static void Test(int a, params IEnumerable<int> b) { System.Console.Write("Called"); } static void Test2<T>(int a, params T[] b) { System.Console.Write("Called2"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(d); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(d)").WithArguments("Program.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9), // (9,9): error CS9218: 'Program.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(d, 1); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(d, 1)").WithArguments("Program.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(9, 9), // (10,9): error CS9218: 'Program.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(d, 2, 3)").WithArguments("Program.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(10, 9), // (11,9): error CS9218: 'Program.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(2, d, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(2, d, 3)").WithArguments("Program.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(11, 9), // (12,9): error CS9218: 'Program.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(2, 3, d); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(2, 3, d)").WithArguments("Program.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(12, 9) ); } [Fact] public void DynamicInvocation_OrdinaryMethod_02_AmbiguousDynamicParamsArgument() { var src1 = """ using System.Collections.Generic; public static class Helpers { public static void Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } """; var comp1Ref = CreateCompilation(src1).EmitToImageReference(); var src2 = """ using static Helpers; class Program { static void Main() { dynamic d = 1; Test(d); } } """; var comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); comp.VerifyEmitDiagnostics(); } [Fact] public void DynamicInvocation_OrdinaryMethod_03_Warning() { var src1 = """ using System.Collections.Generic; public static class Helpers { public static void Test1(params IEnumerable<int> b) => System.Console.Write("Called1"); public static void Test1(System.DateTime b) => System.Console.Write("Called2"); public static void Test2(int x, System.DateTime b) => System.Console.Write("Called3"); public static void Test2(long x, IEnumerable<int> b) => System.Console.Write("Called4"); public static void Test2(byte x, params IEnumerable<int> b) => System.Console.Write("Called5"); public static void Test3(byte x, params IEnumerable<int> b) => System.Console.Write("Called6"); public static void Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); public static void Test4(byte x, params IEnumerable<int> b) => System.Console.Write("Called8"); public static void Test4(byte x, long y, long z) => System.Console.Write("Called9"); } """; var comp1Ref = CreateCompilation(src1).EmitToImageReference(); var src2 = """ using static Helpers; class Program { static void Main() { dynamic d1 = System.DateTime.Now; Test1(d1); // Called2 dynamic d2 = new[] { 1 }; Test1(d2); // Called1 Test2(1, d1); // Called3 Test2(1, d2); // Called5 int x = 1; Test2(x, d1); // Called3 Test2(x, d2); // Called4 dynamic d3 = (byte)1; Test3(d3, 1, 2); // Called7 Test3(d3, x, x); // Called6 dynamic d4 = x; Test4((byte)d3, x, x); // Called8 Test4(d3, x, x); // Called9 Test4(d3, d4, d4); // Called9 } } """; var comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); var expected = new[] { // (21,9): error CS9218: 'Helpers.Test3(byte, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test3(d3, x, x); // Called6 Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test3(d3, x, x)").WithArguments("Helpers.Test3(byte, params System.Collections.Generic.IEnumerable<int>)").WithLocation(21, 9) }; comp.VerifyDiagnostics(expected); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); comp.VerifyDiagnostics(expected); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (21,19): error CS1503: Argument 2: cannot convert from 'int' to 'byte' // Test3(d3, x, x); // Called6 Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "byte").WithLocation(21, 19), // (21,22): error CS1503: Argument 3: cannot convert from 'int' to 'byte' // Test3(d3, x, x); // Called6 Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("3", "int", "byte").WithLocation(21, 22) ); var src3 = """ using static Helpers; class Program { static void Main() { dynamic d1 = System.DateTime.Now; Test1(d1); // Called2 dynamic d2 = new[] { 1 }; Test1(d2); // Called1 Test2(1, d1); // Called3 Test2(1, d2); // Called5 int x = 1; Test2(x, d1); // Called3 Test2(x, d2); // Called4 dynamic d3 = (byte)1; dynamic d4 = x; Test4(d3, x, x); // Called9 Test4(d3, d4, d4); // Called9 } } """; comp = CreateCompilation(src3, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); CompileAndVerify( comp, expectedOutput: @"Called2Called1Called3Called5Called3Called4Called9Called9"). VerifyDiagnostics(); } [Fact] public void DynamicInvocation_OrdinaryMethod_04() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test(d, 2); } static void Test(int a, params IEnumerable<int> b) { System.Console.Write("Called {0}", b is not null); } static void Test(int a, System.DateTime b) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(d, 2); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(d, 2)").WithArguments("Program.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_OrdinaryMethod_05() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test(d, 2, 3); } static void Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test(params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(d, 2, 3)").WithArguments("Program.Test(params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_OrdinaryMethod_06_TypeArgumentInferenceError() { var src0 = """ using System.Collections.Generic; public class Program { public static void Test<T>(params IEnumerable<T> b) { } } """; var comp0Ref = CreateCompilation(src0).EmitToImageReference(); var src1 = """ using static Program; class P { static void Main() { dynamic d = 1; Test(d, 2, 3); Test(d); } } """; var comp1 = CreateCompilation(src1, references: [comp0Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp1.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test<T>(params IEnumerable<T>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(d, 2, 3)").WithArguments("Program.Test<T>(params System.Collections.Generic.IEnumerable<T>)").WithLocation(8, 9) ); comp1 = CreateCompilation(src1, references: [comp0Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); comp1.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test<T>(params IEnumerable<T>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(d, 2, 3)").WithArguments("Program.Test<T>(params System.Collections.Generic.IEnumerable<T>)").WithLocation(8, 9) ); comp1 = CreateCompilation(src1, references: [comp0Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); comp1.VerifyEmitDiagnostics( // (8,9): error CS1501: No overload for method 'Test' takes 3 arguments // Test(d, 2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "3").WithLocation(8, 9) ); var src2 = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test<int>(d, 2, 3); } static void Test<T>(params IEnumerable<T> b) { System.Console.Write("Called"); } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp2.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test<int>(params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test<int>(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test<int>(d, 2, 3)").WithArguments("Program.Test<int>(params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_OrdinaryMethod_07_TypeArgumentInferenceError() { var src0 = """ using System.Collections.Generic; public class Program { public static void Test<T>(T a, params IEnumerable<long> b) { } } """; var comp0Ref = CreateCompilation(src0).EmitToImageReference(); var src1 = """ using static Program; class P { static void Main() { dynamic d = 1; Test(0, d, 2, 3); Test(0, d); } } """; var comp1 = CreateCompilation(src1, references: [comp0Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp1.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test<T>(T, params IEnumerable<long>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(0, d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(0, d, 2, 3)").WithArguments("Program.Test<T>(T, params System.Collections.Generic.IEnumerable<long>)").WithLocation(8, 9) ); comp1 = CreateCompilation(src1, references: [comp0Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); comp1.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test<T>(T, params IEnumerable<long>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test(0, d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test(0, d, 2, 3)").WithArguments("Program.Test<T>(T, params System.Collections.Generic.IEnumerable<long>)").WithLocation(8, 9) ); comp1 = CreateCompilation(src1, references: [comp0Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); comp1.VerifyEmitDiagnostics( // (8,9): error CS1501: No overload for method 'Test' takes 4 arguments // Test(0, d, 2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "4").WithLocation(8, 9) ); var src2 = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test<int>(0, d, 2, 3); } static void Test<T>(T a, params IEnumerable<long> b) { System.Console.Write("Called"); } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp2.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test<int>(int, params IEnumerable<long>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // Test<int>(0, d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "Test<int>(0, d, 2, 3)").WithArguments("Program.Test<int>(int, params System.Collections.Generic.IEnumerable<long>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_OrdinaryMethod_08_HideByOverride() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new C2().Test(d, 2, 3); } } class C1 { public virtual void Test(params IEnumerable<int> b){} } class C2 : C1 { public override void Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'C1.Test(params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new C2().Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new C2().Test(d, 2, 3)").WithArguments("C1.Test(params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_OrdinaryMethod_09_HideBySignature() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new C2().Test<int>(d, 2, 3); } } class C1 { public void Test<T>(params IEnumerable<T> b){} } class C2 : C1 { new public void Test<T>(params IEnumerable<T> b) { System.Console.Write("Called"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'C2.Test<int>(params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new C2().Test<int>(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new C2().Test<int>(d, 2, 3)").WithArguments("C2.Test<int>(params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_OrdinaryMethod_10_HideBySignature() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new C2().Test(d, 2, 3); } } class C0 { public virtual void Test(params IEnumerable<int> b){} } class C1 : C0 { public override void Test(params IEnumerable<int> b){} } class C2 : C1 { new public void Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'C2.Test(params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new C2().Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new C2().Test(d, 2, 3)").WithArguments("C2.Test(params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_OrdinaryMethod_11_HideBySignature() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new C3().Test(d, 2, 3); } } class C1 { public void Test(params IEnumerable<int> b){} } class C2 : C1 { new public virtual void Test(params IEnumerable<int> b) {} } class C3 : C2 { public override void Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'C2.Test(params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new C3().Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new C3().Test(d, 2, 3)").WithArguments("C2.Test(params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_OrdinaryMethod_12_HideByName() { var src1 = """ using System.Collections.Generic; public class C1 { public void Test(params IEnumerable<int> b){} } """; var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseDll); var src2 = """ Public Class C2 Inherits C1 Public Shadows Sub Test(ParamArray b As Long()) System.Console.Write("Called") End Sub End Class """; MetadataReference comp1Ref = comp1.EmitToImageReference(); var comp2 = CreateVisualBasicCompilation(src2, referencedAssemblies: TargetFrameworkUtil.GetReferences(TargetFramework.Standard).Concat(comp1Ref)); var src = """ class Program { static void Main() { dynamic d = 1; new C2().Test(d, 2, 3); } } """; var comp = CreateCompilation(src, references: new[] { comp1Ref, comp2.EmitToImageReference() }, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] public void DynamicInvocation_OrdinaryMethod_13_DoNotHideByApplicability() { var src = """ class Program { static void Main() { dynamic d = 1L; new C2().Test(d); } } class C1 { public void Test(long a) { System.Console.Write("long"); } } class C2 : C1 { public void Test(int b) { System.Console.Write("int"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, expectedOutput: @"long").VerifyDiagnostics(); } [Fact] public void DynamicInvocation_OrdinaryMethod_14_DoNotFilterBasedOnBetterFunctionMember() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1L; new C1().Test(1, d, 2); } } class C1 { public void Test(long a1, params IEnumerable<long> a2) { System.Console.Write("long"); } public void Test(int b1, int b2, int b3) { System.Console.Write("int"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); } [Fact] public void DynamicInvocation_OrdinaryMethod_15_Warning() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d1 = 1; Test1((int)d1); try { Test1(d1); } catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) { System.Console.Write("Failed"); } } static void Test1(params IEnumerable<int> b) => System.Console.Write("Called1"); static void Test1(System.DateTime b) => System.Console.Write("Called2"); } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, expectedOutput: @"Called1Failed"). VerifyDiagnostics(); } [Fact] public void DynamicInvocation_LocalFunction_01() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test(d); Test(d, 1); Test(d, 2, 3); Test(2, d, 3); Test(2, 3, d); Test(2, [3, d]); Test2(d); Test2(d, 1); Test2(d, 2, 3); Test2(2, d, 3); Test2(2, 3, d); Test2(d, [3, 4]); void Test(int a, params IEnumerable<int> b) { System.Console.Write("Called"); } void Test2(int a, params int[] b) { System.Console.Write("Called2"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2").VerifyDiagnostics(); } [Fact] public void DynamicInvocation_LocalFunction_02_AmbiguousDynamicParamsArgument() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test(d); static void Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,14): error CS8108: Cannot pass argument with dynamic type to params parameter 'b' of local function 'Test'. // Test(d); Diagnostic(ErrorCode.ERR_DynamicLocalFunctionParamsParameter, "d").WithArguments("b", "Test").WithLocation(8, 14) ); } [Fact] public void DynamicInvocation_LocalFunction_06_TypeArgumentInferenceError() { var src1 = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test(d, 2, 3); void Test<T>(params IEnumerable<T> b) { System.Console.Write("Called"); } } } """; var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp1.VerifyDiagnostics( // (8,9): error CS8322: Cannot pass argument with dynamic type to generic local function 'Test' with inferred type arguments. // Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicLocalFunctionTypeParameter, "Test(d, 2, 3)").WithArguments("Test").WithLocation(8, 9) ); var src2 = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test<int>(d, 2, 3); void Test<T>(params IEnumerable<T> b) { System.Console.Write("Called"); } } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp2, expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] public void DynamicInvocation_LocalFunction_07_TypeArgumentInferenceError() { var src1 = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test(0, d, 2, 3); void Test<T>(T a, params IEnumerable<long> b) { System.Console.Write("Called"); } } } """; var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp1.VerifyDiagnostics( // (8,9): error CS8322: Cannot pass argument with dynamic type to generic local function 'Test' with inferred type arguments. // Test(0, d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicLocalFunctionTypeParameter, "Test(0, d, 2, 3)").WithArguments("Test").WithLocation(8, 9) ); var src2 = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; Test<int>(0, d, 2, 3); void Test<T>(T a, params IEnumerable<long> b) { System.Console.Write("Called"); } } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp2, expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] public void DynamicInvocation_Delegate_01() { var src = """ using System.Collections.Generic; class Program { static void Main() { D test = Test; dynamic d = 1; test(d); test(d, 1); test(d, 2, 3); test(2, d, 3); test(2, 3, d); test(2, [3, d]); D2 test2 = Test2; test2(d); test2(d, d); test2(d, 1); test2(d, 2, 3); test2(2, d, 3); test2(2, 3, d); void Test(int a, IEnumerable<int> b) { System.Console.Write("Called"); } void Test2(int a, int[] b) { System.Console.Write("Called2"); } } } delegate void D(int a, params IEnumerable<int> b); delegate void D2(int a, params int[] b); """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (9,9): error CS9218: 'D.Invoke(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // test(d); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "test(d)").WithArguments("D.Invoke(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(9, 9), // (10,9): error CS9218: 'D.Invoke(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // test(d, 1); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "test(d, 1)").WithArguments("D.Invoke(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(10, 9), // (11,9): error CS9218: 'D.Invoke(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "test(d, 2, 3)").WithArguments("D.Invoke(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(11, 9), // (12,9): error CS9218: 'D.Invoke(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // test(2, d, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "test(2, d, 3)").WithArguments("D.Invoke(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(12, 9), // (13,9): error CS9218: 'D.Invoke(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // test(2, 3, d); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "test(2, 3, d)").WithArguments("D.Invoke(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(13, 9) ); } [Fact] public void DynamicInvocation_Delegate_02_AmbiguousDynamicParamsArgument() { var src1 = """ using System.Collections.Generic; public delegate void D(params IEnumerable<int> b); """; var comp1Ref = CreateCompilation(src1).EmitToImageReference(); var src2 = """ using System.Collections.Generic; class Program { static void Main() { D test = Test; dynamic d = 1; test(d); static void Test(IEnumerable<int> b) { System.Console.Write("Called"); } } } """; var comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); comp.VerifyEmitDiagnostics(); } [Fact] public void DynamicInvocation_Indexer_01() { var src = """ using System.Collections.Generic; class Program { static void Main() { var c1 = new C1(); dynamic d = 1; _ = c1[d]; _ = c1[d, 1]; _ = c1[d, 2, 3]; _ = c1[2, d, 3]; _ = c1[2, 3, d]; var c2 = new C2(); _ = c2[d]; _ = c2[d, d]; _ = c2[d, 1]; _ = c2[d, 2, 3]; _ = c2[2, d, 3]; _ = c2[2, 3, d]; } } class C1 { public int this[int a, params IEnumerable<int> b] { get { System.Console.Write("Called"); return 0; } } } class C2 { public int this[int a, params int[] b] { get { System.Console.Write("Called2"); return 0; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (9,13): error CS9218: 'C1.this[int, params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = c1[d]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "c1[d]").WithArguments("C1.this[int, params System.Collections.Generic.IEnumerable<int>]").WithLocation(9, 13), // (10,13): error CS9218: 'C1.this[int, params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = c1[d, 1]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "c1[d, 1]").WithArguments("C1.this[int, params System.Collections.Generic.IEnumerable<int>]").WithLocation(10, 13), // (11,13): error CS9218: 'C1.this[int, params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = c1[d, 2, 3]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "c1[d, 2, 3]").WithArguments("C1.this[int, params System.Collections.Generic.IEnumerable<int>]").WithLocation(11, 13), // (12,13): error CS9218: 'C1.this[int, params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = c1[2, d, 3]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "c1[2, d, 3]").WithArguments("C1.this[int, params System.Collections.Generic.IEnumerable<int>]").WithLocation(12, 13), // (13,13): error CS9218: 'C1.this[int, params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = c1[2, 3, d]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "c1[2, 3, d]").WithArguments("C1.this[int, params System.Collections.Generic.IEnumerable<int>]").WithLocation(13, 13) ); } [Fact] public void DynamicInvocation_Indexer_02_AmbiguousDynamicParamsArgument() { var src1 = """ using System.Collections.Generic; public class Program { public int this[params IEnumerable<int> b] { get { System.Console.Write("Called"); return 0; } } } """; var comp1Ref = CreateCompilation(src1).EmitToImageReference(); var src2 = """ class P { static void Main() { dynamic d = 1; _ = new Program()[d]; } } """; var comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); comp.VerifyEmitDiagnostics(); } [Fact] public void DynamicInvocation_Indexer_03_Warning() { var src1 = """ using System.Collections.Generic; public class Test1 { public int this[params IEnumerable<int> b] { get { System.Console.Write("Called1"); return 0; } } public int this[System.DateTime b] { get { System.Console.Write("Called2"); return 0; } } } public class Test2 { public int this[int x, System.DateTime b] { get { System.Console.Write("Called3"); return 0; } } public int this[long x, IEnumerable<int> b] { get { System.Console.Write("Called4"); return 0; } } public int this[byte x, params IEnumerable<int> b] { get { System.Console.Write("Called5"); return 0; } } } public class Test3 { public int this[byte x, params IEnumerable<int> b] { get { System.Console.Write("Called6"); return 0; } } public int this[byte x, byte y, byte z] { get { System.Console.Write("Called7"); return 0; } } } public class Test4 { public int this[byte x, params IEnumerable<int> b] { get { System.Console.Write("Called8"); return 0; } } public int this[byte x, long y, long z] { get { System.Console.Write("Called9"); return 0; } } } """; var comp1Ref = CreateCompilation(src1).EmitToImageReference(); var src2 = """ class Program { static void Main() { dynamic d1 = System.DateTime.Now; _ = new Test1()[d1]; // Called2 dynamic d2 = new[] { 1 }; _ = new Test1()[d2]; // Called1 _ = new Test2()[1, d1]; // Called3 _ = new Test2()[1, d2]; // Called5 int x = 1; _ = new Test2()[x, d1]; // Called3 _ = new Test2()[x, d2]; // Called4 dynamic d3 = (byte)1; _ = new Test3()[d3, 1, 2]; // Called7 _ = new Test3()[d3, x, x]; // Called6 dynamic d4 = x; _ = new Test4()[(byte)d3, x, x]; // Called8 _ = new Test4()[d3, x, x]; // Called9 _ = new Test4()[d3, d4, d4]; // Called9 } } """; var comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); var expected = new[] { // (19,13): error CS9218: 'Test3.this[byte, params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = new Test3()[d3, x, x]; // Called6 Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test3()[d3, x, x]").WithArguments("Test3.this[byte, params System.Collections.Generic.IEnumerable<int>]").WithLocation(19, 13) }; comp.VerifyDiagnostics(expected); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); comp.VerifyDiagnostics(expected); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (19,29): error CS1503: Argument 2: cannot convert from 'int' to 'byte' // _ = new Test3()[d3, x, x]; // Called6 Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "byte").WithLocation(19, 29), // (19,32): error CS1503: Argument 3: cannot convert from 'int' to 'byte' // _ = new Test3()[d3, x, x]; // Called6 Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("3", "int", "byte").WithLocation(19, 32) ); var src3 = """ class Program { static void Main() { dynamic d1 = System.DateTime.Now; _ = new Test1()[d1]; // Called2 dynamic d2 = new[] { 1 }; _ = new Test1()[d2]; // Called1 _ = new Test2()[1, d1]; // Called3 _ = new Test2()[1, d2]; // Called5 int x = 1; _ = new Test2()[x, d1]; // Called3 _ = new Test2()[x, d2]; // Called4 dynamic d3 = (byte)1; dynamic d4 = x; _ = new Test4()[d3, x, x]; // Called9 _ = new Test4()[d3, d4, d4]; // Called9 } } """; comp = CreateCompilation(src3, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); CompileAndVerify( comp, expectedOutput: @"Called2Called1Called3Called5Called3Called4Called9Called9"). VerifyDiagnostics(); } [Fact] public void DynamicInvocation_Indexer_04() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; _ = new Program()[d, 2]; } int this[int a, params IEnumerable<int> b] { get { System.Console.Write("Called {0}", b is not null); return 0; } } int this[int a, System.DateTime b] => 0; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,13): error CS9218: 'Program.this[int, params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = new Program()[d, 2]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Program()[d, 2]").WithArguments("Program.this[int, params System.Collections.Generic.IEnumerable<int>]").WithLocation(8, 13) ); } [Fact] public void DynamicInvocation_Indexer_05() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; _ = new Program()[d, 2, 3]; } int this[params IEnumerable<int> b] { get { System.Console.Write("Called"); return 0; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,13): error CS9218: 'Program.this[params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = new Program()[d, 2, 3]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Program()[d, 2, 3]").WithArguments("Program.this[params System.Collections.Generic.IEnumerable<int>]").WithLocation(8, 13) ); } [Fact] public void DynamicInvocation_Indexer_08_HideByOverride() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; _ = new C2<int>()[d, 2, 3]; } } class C1<T> { public virtual T this[params IEnumerable<T> b] => default; } class C2<T> : C1<T> { public override T this[params IEnumerable<T> b] { get { System.Console.Write("Called"); return default; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,13): error CS9218: 'C1<int>.this[params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = new C2<int>()[d, 2, 3]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new C2<int>()[d, 2, 3]").WithArguments("C1<int>.this[params System.Collections.Generic.IEnumerable<int>]").WithLocation(8, 13) ); } [Fact] public void DynamicInvocation_Indexer_09_HideBySignature() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; _ = new C2<int>()[d, 2, 3]; } } class C1<T> { public T this[params IEnumerable<T> b] => default; } class C2<T> : C1<T> { new public T this[params IEnumerable<T> b] { get { System.Console.Write("Called"); return default; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,13): error CS9218: 'C2<int>.this[params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = new C2<int>()[d, 2, 3]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new C2<int>()[d, 2, 3]").WithArguments("C2<int>.this[params System.Collections.Generic.IEnumerable<int>]").WithLocation(8, 13) ); } [Fact] public void DynamicInvocation_Indexer_10_HideBySignature() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; _ = new C2<int>()[d, 2, 3]; } } class C0<T> { public virtual T this[params IEnumerable<T> b] => default; } class C1<T> : C0<T> { public override T this[params IEnumerable<T> b] => default; } class C2<T> : C1<T> { new public T this[params IEnumerable<T> b] { get { System.Console.Write("Called"); return default; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,13): error CS9218: 'C2<int>.this[params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = new C2<int>()[d, 2, 3]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new C2<int>()[d, 2, 3]").WithArguments("C2<int>.this[params System.Collections.Generic.IEnumerable<int>]").WithLocation(8, 13) ); } [Fact] public void DynamicInvocation_Indexer_11_HideBySignature() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; _ = new C3<int>()[d, 2, 3]; } } class C1<T> { public T this[params IEnumerable<T> b] => default; } class C2<T> : C1<T> { new public virtual T this[params IEnumerable<T> b] => default; } class C3<T> : C2<T> { public override T this[params IEnumerable<T> b] { get { System.Console.Write("Called"); return default; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,13): error CS9218: 'C2<int>.this[params IEnumerable<int>]' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // _ = new C3<int>()[d, 2, 3]; Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new C3<int>()[d, 2, 3]").WithArguments("C2<int>.this[params System.Collections.Generic.IEnumerable<int>]").WithLocation(8, 13) ); } [Fact] public void DynamicInvocation_Indexer_12_HideByName() { var src1 = """ using System.Collections.Generic; public class C1 { public int this[int x, params IEnumerable<int> b] => default; } """; var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseDll); var src2 = """ Public Class C2 Inherits C1 Public Shadows Readonly Default Property Item(x as Integer, ParamArray b As Long()) As Integer Get System.Console.Write("Called") Return 0 End Get End Property End Class """; MetadataReference comp1Ref = comp1.EmitToImageReference(); var comp2 = CreateVisualBasicCompilation(src2, referencedAssemblies: TargetFrameworkUtil.GetReferences(TargetFramework.Standard).Concat(comp1Ref)); var src = """ class Program { static void Main() { dynamic d = 1; _ = new C2()[4, d, 2, 3]; } } """; var comp = CreateCompilation(src, references: new[] { comp1Ref, comp2.EmitToImageReference() }, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] public void DynamicInvocation_Indexer_13_DoNotHideByApplicability() { var src = """ class Program { static void Main() { dynamic d = 1L; _ = new C2()[d]; } } class C1 { public long this[long a] { get { System.Console.Write("long"); return a; } } } class C2 : C1 { public int this[int b] { get { System.Console.Write("int"); return b; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, expectedOutput: @"long").VerifyDiagnostics(); } [Fact] public void DynamicInvocation_Indexer_14_DoNotFilterBasedOnBetterFunctionMember() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1L; _ = new C1()[1, d, 2]; } } class C1 { public long this[long a1, params IEnumerable<long> a2] { get { System.Console.Write("long"); return a1; } } public int this[int b1, int b2, int b3] { get { System.Console.Write("int"); return b1; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); } [Fact] public void DynamicInvocation_Constructor_01() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new Test(d); new Test(d, 1); new Test(d, 2, 3); new Test(2, d, 3); new Test(2, 3, d); new Test(d, [3, 4]); new Test2(d); new Test2(d, d); new Test2(d, 1); new Test2(d, 2, 3); new Test2(2, d, 3); new Test2(2, 3, d); new Test2(d, [3, 4]); } class Test { public Test(int a, params IEnumerable<int> b) { System.Console.Write("Called"); } } class Test2 { public Test2(int a, params int[] b) { System.Console.Write("Called2"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new Test(d); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test(d)").WithArguments("Program.Test.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9), // (9,9): error CS9218: 'Program.Test.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new Test(d, 1); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test(d, 1)").WithArguments("Program.Test.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(9, 9), // (10,9): error CS9218: 'Program.Test.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test(d, 2, 3)").WithArguments("Program.Test.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(10, 9), // (11,9): error CS9218: 'Program.Test.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new Test(2, d, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test(2, d, 3)").WithArguments("Program.Test.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(11, 9), // (12,9): error CS9218: 'Program.Test.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new Test(2, 3, d); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test(2, 3, d)").WithArguments("Program.Test.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(12, 9), // (13,21): error CS9176: There is no target type for the collection expression. // new Test(d, [3, 4]); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[3, 4]").WithLocation(13, 21), // (21,22): error CS9176: There is no target type for the collection expression. // new Test2(d, [3, 4]); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[3, 4]").WithLocation(21, 22) ); } [Fact] public void DynamicInvocation_Constructor_02_AmbiguousDynamicParamsArgument() { var src1 = """ using System.Collections.Generic; public class Test { public Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } """; var comp1Ref = CreateCompilation(src1).EmitToImageReference(); var src2 = """ class Program { static void Main() { dynamic d = 1; new Test(d); } } """; var comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); comp.VerifyEmitDiagnostics(); } [Fact] public void DynamicInvocation_Constructor_03_Warning() { var src1 = """ using System.Collections.Generic; public class Test1 { public Test1(params IEnumerable<int> b) => System.Console.Write("Called1"); public Test1(System.DateTime b) => System.Console.Write("Called2"); } public class Test2 { public Test2(int x, System.DateTime b) => System.Console.Write("Called3"); public Test2(long x, IEnumerable<int> b) => System.Console.Write("Called4"); public Test2(byte x, params IEnumerable<int> b) => System.Console.Write("Called5"); } public class Test3 { public Test3(byte x, params IEnumerable<int> b) => System.Console.Write("Called6"); public Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); } public class Test4 { public Test4(byte x, params IEnumerable<int> b) => System.Console.Write("Called8"); public Test4(byte x, long y, long z) => System.Console.Write("Called9"); } """; var src2 = """ class Program { static void Main() { dynamic d1 = System.DateTime.Now; new Test1(d1); // Called2 dynamic d2 = new[] { 1 }; new Test1(d2); // Called1 new Test2(1, d1); // Called3 new Test2(1, d2); // Called5 int x = 1; new Test2(x, d1); // Called3 new Test2(x, d2); // Called4 dynamic d3 = (byte)1; new Test3(d3, 1, 2); // Called7 new Test3(d3, x, x); // Called6 dynamic d4 = x; new Test4((byte)d3, x, x); // Called8 new Test4(d3, x, x); // Called9 new Test4(d3, d4, d4); // Called9 } } """; var comp1Ref = CreateCompilation(src1).EmitToImageReference(); var comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); var expected = new[] { // (19,9): error CS9218: 'Test3.Test3(byte, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new Test3(d3, x, x); // Called6 Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test3(d3, x, x)").WithArguments("Test3.Test3(byte, params System.Collections.Generic.IEnumerable<int>)").WithLocation(19, 9) }; comp.VerifyDiagnostics(expected); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); comp.VerifyDiagnostics(expected); comp = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (19,23): error CS1503: Argument 2: cannot convert from 'int' to 'byte' // new Test3(d3, x, x); // Called6 Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "byte").WithLocation(19, 23), // (19,26): error CS1503: Argument 3: cannot convert from 'int' to 'byte' // new Test3(d3, x, x); // Called6 Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("3", "int", "byte").WithLocation(19, 26) ); var src3 = """ class Program { static void Main() { dynamic d1 = System.DateTime.Now; new Test1(d1); // Called2 dynamic d2 = new[] { 1 }; new Test1(d2); // Called1 new Test2(1, d1); // Called3 new Test2(1, d2); // Called5 int x = 1; new Test2(x, d1); // Called3 new Test2(x, d2); // Called4 dynamic d3 = (byte)1; dynamic d4 = x; new Test4(d3, x, x); // Called9 new Test4(d3, d4, d4); // Called9 } } """; comp = CreateCompilation(src3, references: [comp1Ref], targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular12); CompileAndVerify( comp, expectedOutput: @"Called2Called1Called3Called5Called3Called4Called9Called9"). VerifyDiagnostics(); } [Fact] public void DynamicInvocation_Constructor_04() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new Test(d, 2); } class Test { public Test(int a, params IEnumerable<int> b) { System.Console.Write("Called {0}", b is not null); } public Test(int a, System.DateTime b) { } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new Test(d, 2); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test(d, 2)").WithArguments("Program.Test.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_Constructor_05() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new Test(d, 2, 3); } class Test { public Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS9218: 'Program.Test.Test(params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new Test(d, 2, 3); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test(d, 2, 3)").WithArguments("Program.Test.Test(params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_Constructor_14_DoNotFilterBasedOnBetterFunctionMember() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1L; new Test(1, d, 2); } } class Test { public Test(long a1, params IEnumerable<long> a2) { System.Console.Write("long"); } public Test(int b1, int b2, int b3) { System.Console.Write("int"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); } [Fact] public void DynamicInvocation_Constructor_16_Abstract() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new Test(d); } abstract class Test { public Test(int a, params IEnumerable<int> b) { System.Console.Write("Called"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,9): error CS0144: Cannot create an instance of the abstract type or interface 'Program.Test' // new Test(d); Diagnostic(ErrorCode.ERR_NoNewAbstract, "new Test(d)").WithArguments("Program.Test").WithLocation(8, 9), // (8,9): error CS9218: 'Program.Test.Test(int, params IEnumerable<int>)' is applicable only with expanded form of non-array params collection which is not supported during dynamic dispatch. // new Test(d); Diagnostic(ErrorCode.ERR_DynamicDispatchToParamsCollection, "new Test(d)").WithArguments("Program.Test.Test(int, params System.Collections.Generic.IEnumerable<int>)").WithLocation(8, 9) ); } [Fact] public void DynamicInvocation_ConstructorInitializer_01() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new C01(d); new C02(d); new C03(d); new C04(d); new C05(d); new C06(d); new C07(d); new C09(d); new C10(d); new C11(d); new C12(d); new C13(d); } class C01(dynamic d) : Test(d); class C02(dynamic d) : Test(d, 1); class C03(dynamic d) : Test(d, 2, 3); class C04(dynamic d) : Test(2, d, 3); class C05(dynamic d) : Test(2, 3, d); class C06(dynamic d) : Test(d, [3, 4]); class C07(dynamic d) : Test2(d); class C09(dynamic d) : Test2(d, 1); class C10(dynamic d) : Test2(d, 2, 3); class C11(dynamic d) : Test2(2, d, 3); class C12(dynamic d) : Test2(2, 3, d); class C13(dynamic d) : Test2(d, [3, 4]); class Test { public Test(int a, params IEnumerable<int> b) { System.Console.Write("Called"); } } class Test2 { public Test2(int a, params int[] b) { System.Console.Write("Called2"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // class C01(dynamic d) : Test(d); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d)").WithLocation(23, 32), // (24,32): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C02(dynamic d) : Test(d, 1); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d, 1)").WithLocation(24, 32), // (25,32): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C03(dynamic d) : Test(d, 2, 3); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d, 2, 3)").WithLocation(25, 32), // (26,32): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C04(dynamic d) : Test(2, d, 3); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(2, d, 3)").WithLocation(26, 32), // (27,32): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C05(dynamic d) : Test(2, 3, d); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(2, 3, d)").WithLocation(27, 32), // (28,32): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C06(dynamic d) : Test(d, [3, 4]); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d, [3, 4])").WithLocation(28, 32), // (30,33): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C07(dynamic d) : Test2(d); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d)").WithLocation(30, 33), // (31,33): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C09(dynamic d) : Test2(d, 1); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d, 1)").WithLocation(31, 33), // (32,33): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C10(dynamic d) : Test2(d, 2, 3); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d, 2, 3)").WithLocation(32, 33), // (33,33): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C11(dynamic d) : Test2(2, d, 3); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(2, d, 3)").WithLocation(33, 33), // (34,33): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C12(dynamic d) : Test2(2, 3, d); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(2, 3, d)").WithLocation(34, 33), // (35,33): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C13(dynamic d) : Test2(d, [3, 4]); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d, [3, 4])").WithLocation(35, 33) ); } [Fact] public void DynamicInvocation_ConstructorInitializer_02_AmbiguousDynamicParamsArgument() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new C(d); } class C(dynamic d) : Test(d); class Test { public Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (11,30): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C(dynamic d) : Test(d); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d)").WithLocation(11, 30) ); } [Fact] public void DynamicInvocation_ConstructorInitializer_03_MultipleCandidates() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d1 = System.DateTime.Now; new C01(d1); dynamic d2 = new[] { 1 }; new C02(d2); new C03(d1); new C04(d2); int x = 1; new C05(x, d1); new C06(x, d2); dynamic d3 = (byte)1; new C07(d3); new C08(d3, x); dynamic d4 = x; new C09(d3, x); new C10(d3, x); new C11(d3, d4); } class C01(dynamic d1) : Test1(d1); class C02(dynamic d2) : Test1(d2); class C03(dynamic d1) : Test2(1, d1); class C04(dynamic d2) : Test2(1, d2); class C05(int x, dynamic d1) : Test2(x, d1); class C06(int x, dynamic d2) : Test2(x, d2); class C07(dynamic d3) : Test3(d3, 1, 2); class C08(int x, dynamic d3) : Test3(d3, x, x); // Called6 class C09(dynamic d3, int x) : Test4((byte)d3, x, x); // Called8 class C10(dynamic d3, int x) : Test4(d3, x, x); class C11(dynamic d3, dynamic d4) : Test4(d3, d4, d4); class Test1 { public Test1(params IEnumerable<int> b) => System.Console.Write("Called1"); public Test1(System.DateTime b) => System.Console.Write("Called2"); } class Test2 { public Test2(int x, System.DateTime b) => System.Console.Write("Called3"); public Test2(long x, IEnumerable<int> b) => System.Console.Write("Called4"); public Test2(byte x, params IEnumerable<int> b) => System.Console.Write("Called5"); } class Test3 { public Test3(byte x, params IEnumerable<int> b) => System.Console.Write("Called6"); public Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); } class Test4 { public Test4(byte x, params IEnumerable<int> b) => System.Console.Write("Called8"); public Test4(byte x, long y, long z) => System.Console.Write("Called9"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (29,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C01(dynamic d1) : Test1(d1); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d1)").WithLocation(29, 34), // (31,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C02(dynamic d2) : Test1(d2); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d2)").WithLocation(31, 34), // (32,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C03(dynamic d1) : Test2(1, d1); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(1, d1)").WithLocation(32, 34), // (33,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C04(dynamic d2) : Test2(1, d2); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(1, d2)").WithLocation(33, 34), // (35,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C05(int x, dynamic d1) : Test2(x, d1); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(x, d1)").WithLocation(35, 41), // (36,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C06(int x, dynamic d2) : Test2(x, d2); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(x, d2)").WithLocation(36, 41), // (38,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C07(dynamic d3) : Test3(d3, 1, 2); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, 1, 2)").WithLocation(38, 34), // (39,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C08(int x, dynamic d3) : Test3(d3, x, x); // Called6 Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, x, x)").WithLocation(39, 41), // (42,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C10(dynamic d3, int x) : Test4(d3, x, x); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, x, x)").WithLocation(42, 41), // (43,46): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C11(dynamic d3, dynamic d4) : Test4(d3, d4, d4); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, d4, d4)").WithLocation(43, 46) ); } [Fact] public void DynamicInvocation_ConstructorInitializer_04() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new C(d); } class C(dynamic d) : Test(d, 2); class Test { public Test(int a, params IEnumerable<int> b) { System.Console.Write("Called {0}", b is not null); } public Test(int a, System.DateTime b) { } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (11,30): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C(dynamic d) : Test(d, 2); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d, 2)").WithLocation(11, 30) ); } [Fact] public void DynamicInvocation_ConstructorInitializer_05() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1; new C(d); } class C(dynamic d) : Test(d, 2, 3); class Test { public Test(params IEnumerable<int> b) { System.Console.Write("Called"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (11,30): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C(dynamic d) : Test(d, 2, 3); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d, 2, 3)").WithLocation(11, 30) ); } [Fact] public void DynamicInvocation_ConstructorInitializer_14_DoNotFilterBasedOnBetterFunctionMember() { var src = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 1L; new C(d); } } class C(dynamic d) : Test(1, d, 2); class Test { public Test(long a1, params IEnumerable<long> a2) { System.Console.Write("long"); } public Test(int b1, int b2, int b3) { System.Console.Write("int"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (12,26): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. // class C(dynamic d) : Test(1, d, 2); Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(1, d, 2)").WithLocation(12, 26) ); } [Fact] public void ParameterRefSafetyScope_01_RegularMethod() { var template = """ {0} public ref struct Test {{ public {1} Test1({2}) {{ {3} return default; }} public {4}; }} """; VerifyParameterRefSafetyScope(template, memberName: "Test.Test1"); } private void VerifyParameterRefSafetyScope(string template, string memberName) { // var template = """ // {0} // // ref struct Test // {{ // public {1} Test1({2}) // {{ // {3} // }} // // {4}; // }} // """; // // var template = """ // {0} // // class Test // {{ // public {1} Test1(ref {4}, {2}) // {{ // {3} // }} // }} // """; // IEnumerable<T> --------------------------------------------------------------- CSharpCompilation comp; comp = CreateCompilation( string.Format( template, /* usings */ "using System.Collections.Generic;", /* return type */ "IEnumerable<long>", /* parameter */ "params IEnumerable<long> paramsParameter", /* method body */ @" #line 200 F = paramsParameter; ", /* extra */ @"IEnumerable<long> F"), options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); ParameterSymbol p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.None, p); int parameterOrdinal = p.Ordinal; CompileAndVerify( comp, symbolValidator: (m) => verifyScopeOnImport(m, ScopedKind.None)).VerifyDiagnostics(); comp = CreateCompilation( string.Format( template, /* usings */ "using System.Collections.Generic;", /* return type */ "IEnumerable<long>", /* parameter */ @" #line 100 params scoped IEnumerable<long> paramsParameter ", /* method body */ @" #line 200 F = paramsParameter; ", /* extra */ @"IEnumerable<long> F")); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.ScopedValue, p); comp.VerifyDiagnostics( // (100,1): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. // params scoped IEnumerable<long> paramsParameter Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "params scoped IEnumerable<long> paramsParameter").WithLocation(100, 1) ); comp = CreateCompilation( string.Format( template, /* usings */ "using System.Collections.Generic; using System.Diagnostics.CodeAnalysis;", /* return type */ "IEnumerable<long>", /* parameter */ @" #line 100 [UnscopedRef] params IEnumerable<long> paramsParameter ", /* method body */ @" #line 200 F = paramsParameter; ", /* extra */ @"IEnumerable<long> F"), targetFramework: TargetFramework.Net80); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.None, p); comp.VerifyDiagnostics( // (100,2): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // [UnscopedRef] params IEnumerable<long> paramsParameter Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(100, 2) ); comp = CreateCompilation( string.Format( template, /* usings */ "using System.Collections.Generic; using System.Diagnostics.CodeAnalysis;", /* return type */ "IEnumerable<long>", /* parameter */ @" #line 100 [UnscopedRef] params scoped IEnumerable<long> paramsParameter ", /* method body */ @" #line 200 F = paramsParameter; ", /* extra */ @"IEnumerable<long> F"), targetFramework: TargetFramework.Net80); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.None, p); comp.VerifyDiagnostics( // (100,1): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. // [UnscopedRef] params scoped IEnumerable<long> paramsParameter Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "[UnscopedRef] params scoped IEnumerable<long> paramsParameter").WithLocation(100, 1), // (100,2): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // [UnscopedRef] params scoped IEnumerable<long> paramsParameter Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(100, 2) ); // Array -------------------------------------------------------- comp = CreateCompilation( string.Format( template, /* usings */ "", /* return type */ "long[]", /* parameter */ "params long[] paramsParameter", /* method body */ @" #line 200 F = paramsParameter; ", /* extra */ @"long[] F"), options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.None, p); CompileAndVerify( comp, symbolValidator: (m) => verifyScopeOnImport(m, ScopedKind.None)).VerifyDiagnostics(); comp = CreateCompilation( string.Format( template, /* usings */ "", /* return type */ "long[]", /* parameter */ @" #line 100 params scoped long[] paramsParameter ", /* method body */ @" #line 200 F = paramsParameter; ", /* extra */ @"long[] F")); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.ScopedValue, p); comp.VerifyDiagnostics( // (100,1): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. // params scoped long[] paramsParameter Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "params scoped long[] paramsParameter").WithLocation(100, 1) ); comp = CreateCompilation( string.Format( template, /* usings */ "using System.Diagnostics.CodeAnalysis;", /* return type */ "long[]", /* parameter */ @" #line 100 [UnscopedRef] params long[] paramsParameter ", /* method body */ @" #line 200 F = paramsParameter; ", /* extra */ @"long[] F"), targetFramework: TargetFramework.Net80); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.None, p); comp.VerifyDiagnostics( // (100,2): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // [UnscopedRef] params long[] paramsParameter Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(100, 2) ); comp = CreateCompilation( string.Format( template, /* usings */ "using System.Diagnostics.CodeAnalysis;", /* return type */ "long[]", /* parameter */ @" #line 100 [UnscopedRef] params scoped long[] paramsParameter ", /* method body */ @" #line 200 F = paramsParameter; ", /* extra */ @"long[] F"), targetFramework: TargetFramework.Net80); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.None, p); comp.VerifyDiagnostics( // (100,1): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. // [UnscopedRef] params scoped long[] paramsParameter Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "[UnscopedRef] params scoped long[] paramsParameter").WithLocation(100, 1), // (100,2): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // [UnscopedRef] params scoped long[] paramsParameter Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(100, 2) ); // Span ---------------------------------------------------------- comp = CreateCompilation( string.Format( template, /* usings */ "using System;", /* return type */ "Span<long>", /* parameter */ "params Span<long> paramsParameter", /* method body */ @"", /* extra */ @"Span<long> F"), targetFramework: TargetFramework.Net80, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.ScopedValue, p); CompileAndVerify( comp, verify: Verification.Skipped, symbolValidator: (m) => verifyScopeOnImport(m, ScopedKind.ScopedValue)).VerifyDiagnostics(); bool hasBody = p.ContainingSymbol is not MethodSymbol { MethodKind: MethodKind.DelegateInvoke }; if (hasBody) { comp = CreateCompilation( string.Format( template, /* usings */ "using System;", /* return type */ "Span<long>", /* parameter */ "params Span<long> paramsParameter", /* method body */ @" #line 100 F = paramsParameter; ", /* extra */ @"Span<long> F"), targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( // (100,5): error CS8352: Cannot use variable 'params Span<long> paramsParameter' in this context because it may expose referenced variables outside of their declaration scope // F = paramsParameter; Diagnostic(ErrorCode.ERR_EscapeVariable, "paramsParameter").WithArguments("params System.Span<long> paramsParameter").WithLocation(100, 5) ); } comp = CreateCompilation( string.Format( template, /* usings */ "using System;", /* return type */ "Span<long>", /* parameter */ "params scoped Span<long> paramsParameter", /* method body */ @"", /* extra */ @"Span<long> F"), targetFramework: TargetFramework.Net80, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.ScopedValue, p); CompileAndVerify( comp, verify: Verification.Skipped, symbolValidator: (m) => verifyScopeOnImport(m, ScopedKind.ScopedValue)).VerifyDiagnostics(); if (hasBody) { comp = CreateCompilation( string.Format( template, /* usings */ "using System;", /* return type */ "Span<long>", /* parameter */ "params scoped Span<long> paramsParameter", /* method body */ @" #line 100 F = paramsParameter; ", /* extra */ @"Span<long> F"), targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( // (100,5): error CS8352: Cannot use variable 'params Span<long> paramsParameter' in this context because it may expose referenced variables outside of their declaration scope // F = paramsParameter; Diagnostic(ErrorCode.ERR_EscapeVariable, "paramsParameter").WithArguments("params System.Span<long> paramsParameter").WithLocation(100, 5) ); } comp = CreateCompilation( string.Format( template, /* usings */ "using System; using System.Diagnostics.CodeAnalysis;", /* return type */ "Span<long>", /* parameter */ "[UnscopedRef] params Span<long> paramsParameter", /* method body */ @" #line 100 F = paramsParameter; ", /* extra */ @"Span<long> F"), targetFramework: TargetFramework.Net80, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.None, p); CompileAndVerify( comp, verify: Verification.Skipped, symbolValidator: (m) => verifyScopeOnImport(m, ScopedKind.None, expectUnscopedRefAttribute: true)).VerifyDiagnostics(); comp = CreateCompilation( string.Format( template, /* usings */ "using System; using System.Diagnostics.CodeAnalysis;", /* return type */ "Span<long>", /* parameter */ @" #line 100 [UnscopedRef] params scoped Span<long> paramsParameter ", /* method body */ @" #line 200 F = paramsParameter; ", /* extra */ @"Span<long> F"), targetFramework: TargetFramework.Net80); p = GetParamsParameterForRefSafetyScopeTests(comp); assertScope(ScopedKind.None, p); comp.VerifyDiagnostics( // (100,2): error CS9066: UnscopedRefAttribute cannot be applied to parameters that have a 'scoped' modifier. // [UnscopedRef] params scoped Span<long> paramsParameter Diagnostic(ErrorCode.ERR_UnscopedScoped, "UnscopedRef").WithLocation(100, 2) ); // Helpers --------------------------------------------- void verifyScopeOnImport(ModuleSymbol m, ScopedKind expected, bool expectUnscopedRefAttribute = false) { PEModule module = ((PEModuleSymbol)m).Module; var p1 = (PEParameterSymbol)m.GlobalNamespace.GetMember(memberName).GetParameters()[parameterOrdinal]; assertScope(expected, p1); assertAttributes(expected, expectUnscopedRefAttribute, module, p1); if (p1.ContainingSymbol is PropertySymbol prop) { if (prop.GetMethod is MethodSymbol getMethod) { assertAttributes(expected, expectUnscopedRefAttribute, module, (PEParameterSymbol)getMethod.Parameters[p1.Ordinal]); } if (prop.SetMethod is MethodSymbol setMethod) { assertAttributes(expected, expectUnscopedRefAttribute, module, (PEParameterSymbol)setMethod.Parameters[p1.Ordinal]); } } } static void assertAttributes(ScopedKind expected, bool expectUnscopedRefAttribute, PEModule module, PEParameterSymbol p1) { switch (expected) { case ScopedKind.None: if (expectUnscopedRefAttribute) { Assert.Equal("System.Diagnostics.CodeAnalysis.UnscopedRefAttribute", p1.GetAttributes().Single().ToString()); } else { Assert.Empty(p1.GetAttributes()); } Assert.False(module.FindTargetAttribute(p1.Handle, AttributeDescription.ScopedRefAttribute).HasValue); break; case ScopedKind.ScopedValue: Assert.False(expectUnscopedRefAttribute); Assert.Empty(p1.GetAttributes()); Assert.True(module.FindTargetAttribute(p1.Handle, AttributeDescription.ScopedRefAttribute).HasValue); break; default: Assert.False(true); break; } } static void assertScope(ScopedKind scope, ParameterSymbol p) { Assert.Equal(scope, p.EffectiveScope); if (p.ContainingSymbol is PropertySymbol prop) { if (prop.GetMethod is MethodSymbol getMethod) { Assert.Equal(scope, getMethod.Parameters[p.Ordinal].EffectiveScope); } if (prop.SetMethod is MethodSymbol setMethod) { Assert.Equal(scope, setMethod.Parameters[p.Ordinal].EffectiveScope); } } } } private static ParameterSymbol GetParamsParameterForRefSafetyScopeTests(CSharpCompilation comp) { var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var parameterDecl = tree.GetRoot().DescendantNodes().OfType<ParameterSyntax>().Where(p => p.Identifier.ValueText == "paramsParameter").Single(); return model.GetDeclaredSymbol(parameterDecl).GetSymbol<ParameterSymbol>(); } [Fact] public void ParameterRefSafetyScope_02_Constructor() { var template = """ {0} class Test {{ public /*{1}*/ Test(ref {4}, {2}) {{ {3} }} }} """; VerifyParameterRefSafetyScope(template, memberName: "Test..ctor"); } [Fact] public void ParameterRefSafetyScope_03_Delegate() { var template = """ {0} delegate {1} Test({2}); /*{3}*/ /*{4}*/ """; VerifyParameterRefSafetyScope(template, memberName: "Test.Invoke"); } [Fact] public void ParameterRefSafetyScope_04_Property_get() { var template = """ {0} public ref struct Test {{ public {1} this[{2}] {{ get {{ {3} return default; }} }} public {4}; }} """; VerifyParameterRefSafetyScope(template, memberName: "Test." + WellKnownMemberNames.Indexer); } [Fact] public void ParameterRefSafetyScope_05_Property_set() { var template = """ {0} public ref struct Test {{ public {1} this[{2}] {{ set {{ {3} }} }} public {4}; }} """; VerifyParameterRefSafetyScope(template, memberName: "Test." + WellKnownMemberNames.Indexer); } [Fact] public void ParameterRefSafetyScope_06_Property_get_set() { var template = """ {0} public ref struct Test {{ public {1} this[{2}] {{ get {{ {3} return default; }} set {{}} }} public {4}; }} """; VerifyParameterRefSafetyScope(template, memberName: "Test." + WellKnownMemberNames.Indexer); } [Fact] public void ParameterRefSafetyScope_07_Lambda() { var template = """ {0} class Test {{ public void Test2() {{ var d = {1} (ref {4}, {2}) => {{ {3} return default; }}; }} }} """; VerifyParameterRefSafetyScope(template, memberName: "Test.<>c.<Test2>b__0_0"); } [Fact] public void ParameterRefSafetyScope_08_Lambda_Delegate() { var template = """ {0} class Test {{ public void Test2() {{ var d = {1} (ref {4}, {2}) => {{ {3} return default; }}; }} }} """; VerifyParameterRefSafetyScope(template, memberName: "<>f__AnonymousDelegate0.Invoke"); } [Fact] public void ParameterRefSafetyScope_09_LocalFunction() { var template = """ {0} class Test {{ public void Test2() {{ var d = local; {1} local (ref {4}, {2}) {{ {3} return default; }}; }} }} """; VerifyParameterRefSafetyScope(template, memberName: "Test.<Test2>g__local|0_0"); } [Fact] public void ParameterRefSafetyScope_10_Mismatch_Overriding() { var src = @" using System; abstract class C1 { public abstract Span<long> Test1(Span<long> a); public abstract Span<long> Test2(Span<long> a); public abstract Span<long> Test3(Span<long> a); } class C2 : C1 { public override Span<long> Test1(params Span<long> a) => throw null; public override Span<long> Test2(params scoped Span<long> a) => throw null; public override Span<long> Test3(scoped Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.False(comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().IsParams); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.False(comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().IsParams); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics(); } [Fact] public void ParameterRefSafetyScope_11_Mismatch_Overriding() { var src = @" using System; abstract class C1 { public abstract Span<long> Test1(params Span<long> a); public abstract Span<long> Test2(params scoped Span<long> a); public abstract Span<long> Test3(scoped Span<long> a); } class C2 : C1 { public override Span<long> Test1(Span<long> a) => throw null; public override Span<long> Test2(Span<long> a) => throw null; public override Span<long> Test3(Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.True(comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().IsParams); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.True(comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().IsParams); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (13,32): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public override Span<long> Test1(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("a").WithLocation(13, 32), // (16,32): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public override Span<long> Test2(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("a").WithLocation(16, 32), // (19,32): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public override Span<long> Test3(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test3").WithArguments("a").WithLocation(19, 32) ); } [Fact] public void ParameterRefSafetyScope_12_Mismatch_Overriding() { var src = @" using System; using System.Diagnostics.CodeAnalysis; abstract class C1 { public abstract Span<long> Test1(params Span<long> a); public abstract Span<long> Test2(params scoped Span<long> a); public abstract Span<long> Test3(scoped Span<long> a); } class C2 : C1 { public override Span<long> Test1([UnscopedRef] Span<long> a) => throw null; public override Span<long> Test2([UnscopedRef] Span<long> a) => throw null; public override Span<long> Test3([UnscopedRef] Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (14,32): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public override Span<long> Test1([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("a").WithLocation(14, 32), // (14,39): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // public override Span<long> Test1([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(14, 39), // (17,32): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public override Span<long> Test2([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("a").WithLocation(17, 32), // (17,39): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // public override Span<long> Test2([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(17, 39), // (20,32): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public override Span<long> Test3([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test3").WithArguments("a").WithLocation(20, 32), // (20,39): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // public override Span<long> Test3([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(20, 39) ); } [Fact] public void ParameterRefSafetyScope_13_Mismatch_Overriding() { var src = @" using System; using System.Diagnostics.CodeAnalysis; abstract class C1 { public abstract Span<long> Test1(params Span<long> a); public abstract Span<long> Test2(params scoped Span<long> a); public abstract Span<long> Test3(scoped Span<long> a); } class C2 : C1 { public override Span<long> Test1([UnscopedRef] params Span<long> a) => throw null; public override Span<long> Test2([UnscopedRef] params Span<long> a) => throw null; public override Span<long> Test3([UnscopedRef] params Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); Assert.False(comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().IsParams); comp.VerifyDiagnostics( // (14,32): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public override Span<long> Test1([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("a").WithLocation(14, 32), // (17,32): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public override Span<long> Test2([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("a").WithLocation(17, 32), // (20,32): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public override Span<long> Test3([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test3").WithArguments("a").WithLocation(20, 32) ); } [Fact] public void ParameterRefSafetyScope_14_Mismatch_Implementing() { var src = @" using System; interface C1 { Span<long> Test1(Span<long> a); Span<long> Test2(Span<long> a); Span<long> Test3(Span<long> a); } class C2 : C1 { public Span<long> Test1(params Span<long> a) => throw null; public Span<long> Test2(params scoped Span<long> a) => throw null; public Span<long> Test3(scoped Span<long> a) => throw null; } class C3 : C1 { Span<long> C1.Test1(params Span<long> a) => throw null; Span<long> C1.Test2(params scoped Span<long> a) => throw null; Span<long> C1.Test3(scoped Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (25,19): error CS0466: 'C3.C1.Test1(params Span<long>)' should not have a params parameter since 'C1.Test1(Span<long>)' does not // Span<long> C1.Test1(params Span<long> a) Diagnostic(ErrorCode.ERR_ExplicitImplParams, "Test1").WithArguments("C3.C1.Test1(params System.Span<long>)", "C1.Test1(System.Span<long>)").WithLocation(25, 19), // (28,19): error CS0466: 'C3.C1.Test2(params Span<long>)' should not have a params parameter since 'C1.Test2(Span<long>)' does not // Span<long> C1.Test2(params scoped Span<long> a) Diagnostic(ErrorCode.ERR_ExplicitImplParams, "Test2").WithArguments("C3.C1.Test2(params System.Span<long>)", "C1.Test2(System.Span<long>)").WithLocation(28, 19) ); } [Fact] public void ParameterRefSafetyScope_15_Mismatch_Implementing() { var src = @" using System; interface C1 { Span<long> Test1(params Span<long> a); Span<long> Test2(params scoped Span<long> a); Span<long> Test3(scoped Span<long> a); } class C2 : C1 { public Span<long> Test1(Span<long> a) => throw null; public Span<long> Test2(Span<long> a) => throw null; public Span<long> Test3(Span<long> a) => throw null; } class C3 : C1 { Span<long> C1.Test1(Span<long> a) => throw null; Span<long> C1.Test2(Span<long> a) => throw null; Span<long> C1.Test3(Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (13,23): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public Span<long> Test1(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("a").WithLocation(13, 23), // (16,23): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public Span<long> Test2(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("a").WithLocation(16, 23), // (19,23): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public Span<long> Test3(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test3").WithArguments("a").WithLocation(19, 23), // (25,19): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // Span<long> C1.Test1(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("a").WithLocation(25, 19), // (28,19): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // Span<long> C1.Test2(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("a").WithLocation(28, 19), // (31,19): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // Span<long> C1.Test3(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test3").WithArguments("a").WithLocation(31, 19) ); } [Fact] public void ParameterRefSafetyScope_16_Mismatch_Implementing() { var src = @" using System; using System.Diagnostics.CodeAnalysis; interface C1 { Span<long> Test1(params Span<long> a); Span<long> Test2(params scoped Span<long> a); Span<long> Test3(scoped Span<long> a); } class C2 : C1 { public Span<long> Test1([UnscopedRef] Span<long> a) => throw null; public Span<long> Test2([UnscopedRef] Span<long> a) => throw null; public Span<long> Test3([UnscopedRef] Span<long> a) => throw null; } class C3 : C1 { Span<long> C1.Test1([UnscopedRef] Span<long> a) => throw null; Span<long> C1.Test2([UnscopedRef] Span<long> a) => throw null; Span<long> C1.Test3([UnscopedRef] Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (14,23): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public Span<long> Test1([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("a").WithLocation(14, 23), // (14,30): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // public Span<long> Test1([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(14, 30), // (17,23): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public Span<long> Test2([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("a").WithLocation(17, 23), // (17,30): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // public Span<long> Test2([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(17, 30), // (20,23): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public Span<long> Test3([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test3").WithArguments("a").WithLocation(20, 23), // (20,30): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // public Span<long> Test3([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(20, 30), // (26,19): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // Span<long> C1.Test1([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("a").WithLocation(26, 19), // (26,26): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // Span<long> C1.Test1([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(26, 26), // (29,19): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // Span<long> C1.Test2([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("a").WithLocation(29, 19), // (29,26): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // Span<long> C1.Test2([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(29, 26), // (32,19): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // Span<long> C1.Test3([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test3").WithArguments("a").WithLocation(32, 19), // (32,26): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // Span<long> C1.Test3([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(32, 26) ); } [Fact] public void ParameterRefSafetyScope_17_Mismatch_Implementing() { var src = @" using System; using System.Diagnostics.CodeAnalysis; interface C1 { Span<long> Test1(params Span<long> a); Span<long> Test2(params scoped Span<long> a); Span<long> Test3(scoped Span<long> a); } class C2 : C1 { public Span<long> Test1([UnscopedRef] params Span<long> a) => throw null; public Span<long> Test2([UnscopedRef] params Span<long> a) => throw null; public Span<long> Test3([UnscopedRef] params Span<long> a) => throw null; } class C3 : C1 { Span<long> C1.Test1([UnscopedRef] params Span<long> a) => throw null; Span<long> C1.Test2([UnscopedRef] params Span<long> a) => throw null; Span<long> C1.Test3([UnscopedRef] params Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C3.C1.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (14,23): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public Span<long> Test1([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("a").WithLocation(14, 23), // (17,23): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public Span<long> Test2([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("a").WithLocation(17, 23), // (20,23): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // public Span<long> Test3([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test3").WithArguments("a").WithLocation(20, 23), // (26,19): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // Span<long> C1.Test1([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test1").WithArguments("a").WithLocation(26, 19), // (29,19): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // Span<long> C1.Test2([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test2").WithArguments("a").WithLocation(29, 19), // (32,19): error CS0466: 'C3.C1.Test3(params Span<long>)' should not have a params parameter since 'C1.Test3(scoped Span<long>)' does not // Span<long> C1.Test3([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ExplicitImplParams, "Test3").WithArguments("C3.C1.Test3(params System.Span<long>)", "C1.Test3(scoped System.Span<long>)").WithLocation(32, 19), // (32,19): error CS8987: The 'scoped' modifier of parameter 'a' doesn't match overridden or implemented member. // Span<long> C1.Test3([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "Test3").WithArguments("a").WithLocation(32, 19) ); } [Fact] public void ParameterRefSafetyScope_18_Mismatch_Delegate() { var src = @" using System; class C1 { public delegate Span<long> Test1(Span<long> a); public delegate Span<long> Test2(Span<long> a); public delegate Span<long> Test3(Span<long> a); } class C2 { void Test() { C1.Test1 d1 = Test1; C1.Test2 d2 = Test2; C1.Test3 d3 = Test3; } public Span<long> Test1(params Span<long> a) => throw null; public Span<long> Test2(params scoped Span<long> a) => throw null; public Span<long> Test3(scoped Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics(); } [Fact] public void ParameterRefSafetyScope_19_Mismatch_Delegate() { var src = @" using System; class C1 { public delegate Span<long> Test1(params Span<long> a); public delegate Span<long> Test2(params scoped Span<long> a); public delegate Span<long> Test3(scoped Span<long> a); } class C2 { void Test() { C1.Test1 d1 = Test1; C1.Test2 d2 = Test2; C1.Test3 d3 = Test3; } public Span<long> Test1(Span<long> a) => throw null; public Span<long> Test2(Span<long> a) => throw null; public Span<long> Test3(Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (15,23): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'C1.Test1'. // C1.Test1 d1 = Test1; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "Test1").WithArguments("a", "C1.Test1").WithLocation(15, 23), // (16,23): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'C1.Test2'. // C1.Test2 d2 = Test2; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "Test2").WithArguments("a", "C1.Test2").WithLocation(16, 23), // (17,23): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'C1.Test3'. // C1.Test3 d3 = Test3; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "Test3").WithArguments("a", "C1.Test3").WithLocation(17, 23) ); } [Fact] public void ParameterRefSafetyScope_20_Mismatch_Delegate() { var src = @" using System; using System.Diagnostics.CodeAnalysis; class C1 { public delegate Span<long> Test1(params Span<long> a); public delegate Span<long> Test2(params scoped Span<long> a); public delegate Span<long> Test3(scoped Span<long> a); } class C2 { void Test() { C1.Test1 d1 = Test1; C1.Test2 d2 = Test2; C1.Test3 d3 = Test3; } public Span<long> Test1([UnscopedRef] Span<long> a) => throw null; public Span<long> Test2([UnscopedRef] Span<long> a) => throw null; public Span<long> Test3([UnscopedRef] Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (16,23): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'C1.Test1'. // C1.Test1 d1 = Test1; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "Test1").WithArguments("a", "C1.Test1").WithLocation(16, 23), // (17,23): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'C1.Test2'. // C1.Test2 d2 = Test2; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "Test2").WithArguments("a", "C1.Test2").WithLocation(17, 23), // (18,23): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'C1.Test3'. // C1.Test3 d3 = Test3; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "Test3").WithArguments("a", "C1.Test3").WithLocation(18, 23), // (21,30): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // public Span<long> Test1([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(21, 30), // (24,30): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // public Span<long> Test2([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(24, 30), // (27,30): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // public Span<long> Test3([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(27, 30) ); } [Fact] public void ParameterRefSafetyScope_21_Mismatch_Delegate() { var src = @" using System; using System.Diagnostics.CodeAnalysis; class C1 { public delegate Span<long> Test1(params Span<long> a); public delegate Span<long> Test2(params scoped Span<long> a); public delegate Span<long> Test3(scoped Span<long> a); } class C2 { void Test() { C1.Test1 d1 = Test1; C1.Test2 d2 = Test2; C1.Test3 d3 = Test3; } public Span<long> Test1([UnscopedRef] params Span<long> a) => throw null; public Span<long> Test2([UnscopedRef] params Span<long> a) => throw null; public Span<long> Test3([UnscopedRef] params Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3.Invoke").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (16,23): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'C1.Test1'. // C1.Test1 d1 = Test1; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "Test1").WithArguments("a", "C1.Test1").WithLocation(16, 23), // (17,23): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'C1.Test2'. // C1.Test2 d2 = Test2; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "Test2").WithArguments("a", "C1.Test2").WithLocation(17, 23), // (18,23): error CS8986: The 'scoped' modifier of parameter 'a' doesn't match target 'C1.Test3'. // C1.Test3 d3 = Test3; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "Test3").WithArguments("a", "C1.Test3").WithLocation(18, 23) ); } [Fact] public void ParameterRefSafetyScope_22_Mismatch_Partial() { var src = @" using System; partial class C1 { partial void Test1(Span<long> a); partial void Test2(Span<long> a); partial void Test3(Span<long> a); } partial class C1 { partial void Test1(params Span<long> a) { } partial void Test2(params scoped Span<long> a) { } partial void Test3(scoped Span<long> a) { } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test1").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test2").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test3").PartialImplementationPart.Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (13,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test1(params Span<long> a) Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test1").WithLocation(13, 18), // (13,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test1(params Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test1").WithArguments("a").WithLocation(13, 18), // (17,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test2(params scoped Span<long> a) Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test2").WithLocation(17, 18), // (17,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test2(params scoped Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test2").WithArguments("a").WithLocation(17, 18), // (21,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test3(scoped Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test3").WithArguments("a").WithLocation(21, 18) ); } [Fact] public void ParameterRefSafetyScope_23_Mismatch_Partial() { var src = @" using System; partial class C1 { partial void Test1(params Span<long> a); partial void Test2(params scoped Span<long> a); partial void Test3(scoped Span<long> a); } partial class C1 { partial void Test1(Span<long> a) { } partial void Test2(Span<long> a) { } partial void Test3(Span<long> a) { } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test1").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test2").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.NotEqual(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test3").PartialImplementationPart.Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (13,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test1(Span<long> a) Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test1").WithLocation(13, 18), // (13,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test1(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test1").WithArguments("a").WithLocation(13, 18), // (17,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test2(Span<long> a) Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test2").WithLocation(17, 18), // (17,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test2(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test2").WithArguments("a").WithLocation(17, 18), // (21,18): error CS8988: The 'scoped' modifier of parameter 'a' doesn't match partial definition. // partial void Test3(Span<long> a) Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "Test3").WithArguments("a").WithLocation(21, 18) ); } [Fact] public void ParameterRefSafetyScope_24_Mismatch_Partial() { var src = @" using System; using System.Diagnostics.CodeAnalysis; partial class C1 { partial void Test1(params Span<long> a); partial void Test2(params scoped Span<long> a); partial void Test3(scoped Span<long> a); } partial class C1 { partial void Test1([UnscopedRef] Span<long> a) { } partial void Test2([UnscopedRef] Span<long> a) { } partial void Test3([UnscopedRef] Span<long> a) { } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); // The the [UnscopedRef] attribute is applied to the declaration as well, and it cancels the scoped modifier Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope); Assert.Equal(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test1").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.Equal(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test2").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.Equal(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test3").PartialImplementationPart.Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (14,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test1([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test1").WithLocation(14, 18), // (14,25): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // partial void Test1([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(14, 25), // (18,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test2([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test2").WithLocation(18, 18), // (18,25): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // partial void Test2([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(18, 25), // (22,25): error CS9063: UnscopedRefAttribute cannot be applied to this parameter because it is unscoped by default. // partial void Test3([UnscopedRef] Span<long> a) Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedTarget, "UnscopedRef").WithLocation(22, 25) ); } [Fact] public void ParameterRefSafetyScope_25_Mismatch_Partial() { var src = @" using System; using System.Diagnostics.CodeAnalysis; partial class C1 { public partial Span<long> Test1(params Span<long> a); public partial Span<long> Test2(params scoped Span<long> a); public partial Span<long> Test3(scoped Span<long> a); } partial class C1 { public partial Span<long> Test1([UnscopedRef] params Span<long> a) => throw null; public partial Span<long> Test2([UnscopedRef] params Span<long> a) => throw null; public partial Span<long> Test3([UnscopedRef] params Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); // The the [UnscopedRef] attribute is applied to the declaration as well, and it cancels the scoped modifier Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.None, comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope); Assert.Equal(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test1").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.Equal(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test2").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.Equal(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test3").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.Equal(comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test1").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.Equal(comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test2").PartialImplementationPart.Parameters.Single().EffectiveScope); Assert.Equal(comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope, comp.GetMember<MethodSymbol>("C1.Test3").PartialImplementationPart.Parameters.Single().EffectiveScope); comp.VerifyDiagnostics( // (20,31): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // public partial Span<long> Test3([UnscopedRef] params Span<long> a) Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test3").WithLocation(20, 31) ); } [Fact] public void ParameterRefSafetyScope_26_Mismatch_Overriding() { var src = @" using System; abstract class C1 { public abstract Span<long> Test1(params Span<long> a); public abstract Span<long> Test2(params scoped Span<long> a); public abstract Span<long> Test3(scoped Span<long> a); } class C2 : C1 { public override Span<long> Test1(params Span<long> a) => throw null; public override Span<long> Test2(params Span<long> a) => throw null; public override Span<long> Test3(params Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.True(comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().IsParams); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.True(comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().IsParams); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); Assert.False(comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().IsParams); comp.VerifyDiagnostics(); } [Fact] public void ParameterRefSafetyScope_27_Mismatch_Overriding() { var src = @" using System; abstract class C1 { public abstract Span<long> Test1(params Span<long> a); public abstract Span<long> Test2(params scoped Span<long> a); public abstract Span<long> Test3(scoped Span<long> a); } class C2 : C1 { public override Span<long> Test1(scoped Span<long> a) => throw null; public override Span<long> Test2(scoped Span<long> a) => throw null; public override Span<long> Test3(scoped Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.True(comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().IsParams); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.True(comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().IsParams); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); Assert.False(comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().IsParams); comp.VerifyDiagnostics(); } [Fact] public void ParameterRefSafetyScope_28_Mismatch_Overriding() { var src = @" using System; abstract class C1 { public abstract Span<long> Test1(params Span<long> a); public abstract Span<long> Test2(params scoped Span<long> a); public abstract Span<long> Test3(scoped Span<long> a); } class C2 : C1 { public override Span<long> Test1(params scoped Span<long> a) => throw null; public override Span<long> Test2(params scoped Span<long> a) => throw null; public override Span<long> Test3(params scoped Span<long> a) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test1").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().EffectiveScope); Assert.True(comp.GetMember<MethodSymbol>("C2.Test1").Parameters.Single().IsParams); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test2").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().EffectiveScope); Assert.True(comp.GetMember<MethodSymbol>("C2.Test2").Parameters.Single().IsParams); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C1.Test3").Parameters.Single().EffectiveScope); Assert.Equal(ScopedKind.ScopedValue, comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().EffectiveScope); Assert.False(comp.GetMember<MethodSymbol>("C2.Test3").Parameters.Single().IsParams); comp.VerifyDiagnostics(); } [Fact] public void ExpressionTree() { var src = @" using System.Linq.Expressions; class Program { static void Main() { Expression<System.Action> e1 = () => Test(); Expression<System.Action> e2 = () => Test(1); Expression<System.Action> e3 = () => Test(2, 3); Expression<System.Action> e4 = () => Test([]); Expression<System.Action> e5 = () => Test2(); Expression<System.Action> e6 = () => Test2(1); Expression<System.Action> e7 = () => Test2(2, 3); } static void Test(params System.Collections.Generic.IEnumerable<long> a) { } static void Test2(params long[] a) { } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (8,46): error CS9226: An expression tree may not contain an expanded form of non-array params collection parameter. // Expression<System.Action> e1 = () => Test(); Diagnostic(ErrorCode.ERR_ParamsCollectionExpressionTree, "Test()").WithLocation(8, 46), // (9,46): error CS9226: An expression tree may not contain an expanded form of non-array params collection parameter. // Expression<System.Action> e2 = () => Test(1); Diagnostic(ErrorCode.ERR_ParamsCollectionExpressionTree, "Test(1)").WithLocation(9, 46), // (10,46): error CS9226: An expression tree may not contain an expanded form of non-array params collection parameter. // Expression<System.Action> e3 = () => Test(2, 3); Diagnostic(ErrorCode.ERR_ParamsCollectionExpressionTree, "Test(2, 3)").WithLocation(10, 46), // (11,51): error CS9175: An expression tree may not contain a collection expression. // Expression<System.Action> e4 = () => Test([]); Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "[]").WithLocation(11, 51) ); } [Fact] public void MetadataImport_01_Method() { // public class Params // { // static public void Test1(params System.Collections.Generic.IEnumerable<long> a) { System.Console.Write("Test1"); } // static public void Test2(params long[] a) { System.Console.Write("Test2"); } // } string il = @" .class public auto ansi beforefieldinit Params extends [mscorlib]System.Object { .method public hidebysig static void Test1 ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig static void Test2 ( int64[] a ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<MethodSymbol>("Params.Test1").Parameters.Last(); var test2 = comp.GetMember<MethodSymbol>("Params.Test2").Parameters.Last(); VerifyParamsAndAttribute(test1, isParamCollection: true); VerifyParamsAndAttribute(test2, isParamArray: true); Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); VerifyParamsAndAttribute(test1, isParamCollection: true); VerifyParamsAndAttribute(test2, isParamArray: true); AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); AssertEx.Equal("System.ParamArrayAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); comp = CreateCompilationWithIL("", il); test1 = comp.GetMember<MethodSymbol>("Params.Test1").Parameters.Last(); test2 = comp.GetMember<MethodSymbol>("Params.Test2").Parameters.Last(); Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); VerifyParamsAndAttribute(test1, isParamCollection: true); VerifyParamsAndAttribute(test2, isParamArray: true); AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); AssertEx.Equal("System.ParamArrayAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); var src = @" class Program { static void Main() { Params.Test1(1); Params.Test2(2); } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] public void MetadataImport_02_Method() { // public class Params // { // static public void Test1([ParamCollectionAttribute, ParamArrayAttribute] System.Collections.Generic.IEnumerable<long> a) { System.Console.Write("Test1"); } // static public void Test2([ParamCollectionAttribute, ParamArrayAttribute] long[] a) { System.Console.Write("Test2"); } // } string il = @" .class public auto ansi beforefieldinit Params extends [mscorlib]System.Object { .method public hidebysig static void Test1 ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig static void Test2 ( int64[] a ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<MethodSymbol>("Params.Test1").Parameters.Last(); var test2 = comp.GetMember<MethodSymbol>("Params.Test2").Parameters.Last(); VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); var attributes = new[] { "System.ParamArrayAttribute", "System.Runtime.CompilerServices.ParamCollectionAttribute" }; AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); comp = CreateCompilationWithIL("", il); test1 = comp.GetMember<MethodSymbol>("Params.Test1").Parameters.Last(); test2 = comp.GetMember<MethodSymbol>("Params.Test2").Parameters.Last(); Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); var src = @" class Program { static void Main() { Params.Test1(1); Params.Test2(2); } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] public void MetadataImport_03_Method() { // public class Params // { // static public void Test1([ParamArrayAttribute, ParamCollectionAttribute] System.Collections.Generic.IEnumerable<long> a) { System.Console.Write("Test1"); } // static public void Test2([ParamArrayAttribute, ParamCollectionAttribute] long[] a) { System.Console.Write("Test2"); } // } string il = @" .class public auto ansi beforefieldinit Params extends [mscorlib]System.Object { .method public hidebysig static void Test1 ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig static void Test2 ( int64[] a ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<MethodSymbol>("Params.Test1").Parameters.Last(); var test2 = comp.GetMember<MethodSymbol>("Params.Test2").Parameters.Last(); VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); var attributes = new[] { "System.ParamArrayAttribute", "System.Runtime.CompilerServices.ParamCollectionAttribute" }; AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); comp = CreateCompilationWithIL("", il); test1 = comp.GetMember<MethodSymbol>("Params.Test1").Parameters.Last(); test2 = comp.GetMember<MethodSymbol>("Params.Test2").Parameters.Last(); Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); var src = @" class Program { static void Main() { Params.Test1(1); Params.Test2(2); } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] public void MetadataImport_04_Method() { // public class Params // { // static public void Test1([ParamArrayAttribute] System.Collections.Generic.IEnumerable<long> a) { System.Console.Write("Test1"); } // static public void Test2([ParamCollectionAttribute] long[] a) { System.Console.Write("Test2"); } // } string il = @" .class public auto ansi beforefieldinit Params extends [mscorlib]System.Object { .method public hidebysig static void Test1 ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig static void Test2 ( int64[] a ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<MethodSymbol>("Params.Test1").Parameters.Last(); var test2 = comp.GetMember<MethodSymbol>("Params.Test2").Parameters.Last(); VerifyParamsAndAttribute(test1, isParamArray: true); VerifyParamsAndAttribute(test2, isParamCollection: true); Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); VerifyParamsAndAttribute(test1, isParamArray: true); VerifyParamsAndAttribute(test2, isParamCollection: true); AssertEx.Equal("System.ParamArrayAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); comp = CreateCompilationWithIL("", il); test1 = comp.GetMember<MethodSymbol>("Params.Test1").Parameters.Last(); test2 = comp.GetMember<MethodSymbol>("Params.Test2").Parameters.Last(); Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); VerifyParamsAndAttribute(test1, isParamArray: true); VerifyParamsAndAttribute(test2, isParamCollection: true); AssertEx.Equal("System.ParamArrayAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); var src = @" class Program { static void Main() { Params.Test1(1); Params.Test2(2); } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (6,22): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable<long>' // Params.Test1(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable<long>").WithLocation(6, 22), // (7,22): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' // Params.Test2(2); Diagnostic(ErrorCode.ERR_BadArgType, "2").WithArguments("1", "int", "params long[]").WithLocation(7, 22) ); } [Fact] public void MetadataImport_05_Property() { // public class Params1 // { // public int this[params System.Collections.Generic.IEnumerable<long> a] // { // get // { System.Console.Write("Test1"); return 0; } // } // } // public class Params2 // { // public int this[params long[] a] // { // get // { System.Console.Write("Test2"); return 0; } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) { .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>) } } .class public auto ansi beforefieldinit Params2 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( int64[] a ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( int64[] a ) { .get instance int32 Params2::get_Item(int64[]) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember<PropertySymbol>("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamCollection: true); VerifyParams(test2, isParamArray: true); var src = @" class Program { static void Main() { _ = new Params1()[1]; _ = new Params2()[2]; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] public void MetadataImport_06_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable<long> a] // { // [ParamCollectionAttribute, ParamArrayAttribute] get // { System.Console.Write("Test1"); return 0; } // } // } // public class Params2 // { // public int this[long[] a] // { // [ParamCollectionAttribute, ParamArrayAttribute] get // { System.Console.Write("Test2"); return 0; } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) { .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>) } } .class public auto ansi beforefieldinit Params2 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( int64[] a ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( int64[] a ) { .get instance int32 Params2::get_Item(int64[]) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember<PropertySymbol>("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamArray: true, isParamCollection: true); VerifyParams(test2, isParamArray: true, isParamCollection: true); var src = @" class Program { static void Main() { _ = new Params1()[1]; _ = new Params2()[2]; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] public void MetadataImport_07_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable<long> a] // { // [ParamArrayAttribute, ParamCollectionAttribute] get // { System.Console.Write("Test1"); return 0; } // } // } // public class Params2 // { // public int this[long[] a] // { // [ParamArrayAttribute, ParamCollectionAttribute] get // { System.Console.Write("Test2"); return 0; } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) { .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>) } } .class public auto ansi beforefieldinit Params2 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( int64[] a ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( int64[] a ) { .get instance int32 Params2::get_Item(int64[]) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember<PropertySymbol>("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamArray: true, isParamCollection: true); VerifyParams(test2, isParamArray: true, isParamCollection: true); var src = @" class Program { static void Main() { _ = new Params1()[1]; _ = new Params2()[2]; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] public void MetadataImport_08_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable<long> a] // { // [ParamArrayAttribute] get // { System.Console.Write("Test1"); return 0; } // } // } // public class Params2 // { // public int this[long[] a] // { // [ParamCollectionAttribute] get // { System.Console.Write("Test2"); return 0; } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) { .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>) } } .class public auto ansi beforefieldinit Params2 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( int64[] a ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( int64[] a ) { .get instance int32 Params2::get_Item(int64[]) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember<PropertySymbol>("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamArray: true); VerifyParams(test2, isParamCollection: true); var src = @" class Program { static void Main() { _ = new Params1()[1]; _ = new Params2()[2]; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (6,27): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable<long>' // _ = new Params1()[1]; Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable<long>").WithLocation(6, 27), // (7,27): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' // _ = new Params2()[2]; Diagnostic(ErrorCode.ERR_BadArgType, "2").WithArguments("1", "int", "params long[]").WithLocation(7, 27) ); } [Fact] public void MetadataImport_09_Property() { // public class Params1 // { // public int this[params System.Collections.Generic.IEnumerable<long> a] // { // set // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[params long[] a] // { // set // { System.Console.Write("Test2"); } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance void set_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a, int32 'value' ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) { .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>, int32) } } .class public auto ansi beforefieldinit Params2 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance void set_Item ( int64[] a, int32 'value' ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( int64[] a ) { .set instance void Params2::set_Item(int64[], int32) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember<PropertySymbol>("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamCollection: true); VerifyParams(test2, isParamArray: true); var src = @" class Program { static void Main() { new Params1()[1] = 0; new Params2()[2] = 0; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] public void MetadataImport_10_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable<long> a] // { // [ParamCollectionAttribute, ParamArrayAttribute] set // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { // [ParamCollectionAttribute, ParamArrayAttribute] set // { System.Console.Write("Test2"); } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance void set_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a, int32 'value' ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) { .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>, int32) } } .class public auto ansi beforefieldinit Params2 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance void set_Item ( int64[] a, int32 'value' ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( int64[] a ) { .set instance void Params2::set_Item(int64[], int32) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember<PropertySymbol>("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamArray: true, isParamCollection: true); VerifyParams(test2, isParamArray: true, isParamCollection: true); var src = @" class Program { static void Main() { new Params1()[1] = 0; new Params2()[2] = 0; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] public void MetadataImport_11_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable<long> a] // { // [ParamArrayAttribute, ParamCollectionAttribute] set // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { // [ParamArrayAttribute, ParamCollectionAttribute] set // { System.Console.Write("Test2"); } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance void set_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a, int32 'value' ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) { .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>, int32) } } .class public auto ansi beforefieldinit Params2 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance void set_Item ( int64[] a, int32 'value' ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( int64[] a ) { .set instance void Params2::set_Item(int64[], int32) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember<PropertySymbol>("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamArray: true, isParamCollection: true); VerifyParams(test2, isParamArray: true, isParamCollection: true); var src = @" class Program { static void Main() { new Params1()[1] = 0; new Params2()[2] = 0; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] public void MetadataImport_12_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable<long> a] // { // [ParamArrayAttribute] set // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { // [ParamCollectionAttribute] set // { System.Console.Write("Test2"); } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance void set_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a, int32 'value' ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) { .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>, int32) } } .class public auto ansi beforefieldinit Params2 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance void set_Item ( int64[] a, int32 'value' ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( int64[] a ) { .set instance void Params2::set_Item(int64[], int32) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var comp = CreateCompilationWithIL("", il); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember<PropertySymbol>("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamArray: true); VerifyParams(test2, isParamCollection: true); var src = @" class Program { static void Main() { new Params1()[1] = 0; new Params2()[2] = 0; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (6,23): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable<long>' // new Params1()[1] = 0; Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable<long>").WithLocation(6, 23), // (7,23): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' // new Params2()[2] = 0; Diagnostic(ErrorCode.ERR_BadArgType, "2").WithArguments("1", "int", "params long[]").WithLocation(7, 23) ); } [Flags] public enum ParamsAttributes { None = 0, Array = 1, Collection = 2, Both = Array | Collection, } private string GetAttributesIL(ParamsAttributes attributes) { if (attributes == ParamsAttributes.None) { return ""; } string result = @" .param [1] "; if ((attributes & ParamsAttributes.Array) != 0) { result += @" .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) "; } if ((attributes & ParamsAttributes.Collection) != 0) { result += @" .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) "; } return result; } [Theory] [CombinatorialData] public void MetadataImport_13_Property(ParamsAttributes getAttributes, ParamsAttributes setAttributes) { if (getAttributes == setAttributes) { return; } var getAttributesString = GetAttributesIL(getAttributes); var setAttributesString = GetAttributesIL(setAttributes); // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable<long> a] // { // [getAttributes] get // { System.Console.Write("Test1"); return 0; } // [setAttributes] set // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { // [getAttributes] get // { System.Console.Write("Test2"); return 0; } // [setAttributes] set // { System.Console.Write("Test2"); } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) cil managed { " + getAttributesString + @" .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname instance void set_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a, int32 'value' ) cil managed { " + setAttributesString + @" .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> a ) { .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>) .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>, int32) } } .class public auto ansi beforefieldinit Params2 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( int64[] a ) cil managed { " + getAttributesString + @" .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname instance void set_Item ( int64[] a, int32 'value' ) cil managed { " + setAttributesString + @" .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( int64[] a ) { .get instance int32 Params2::get_Item(int64[]) .set instance void Params2::set_Item(int64[], int32) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var src = @" class Program { static void Main() { _ = new Params1()[1]; _ = new Params2()[2]; new Params1()[1] = 0; new Params2()[2] = 0; } } "; var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember<PropertySymbol>("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamArray: (setAttributes & ParamsAttributes.Array) != 0, isParamCollection: (setAttributes & ParamsAttributes.Collection) != 0); VerifyParams(test2, isParamArray: (setAttributes & ParamsAttributes.Array) != 0, isParamCollection: (setAttributes & ParamsAttributes.Collection) != 0); string getModifier = getAttributes == ParamsAttributes.None ? "" : "params "; string setModifier = setAttributes == ParamsAttributes.None ? "" : "params "; comp.VerifyDiagnostics( // (6,13): error CS1545: Property, indexer, or event 'Params1.this[params IEnumerable<long>]' is not supported by the language; try directly calling accessor methods 'Params1.get_Item(params IEnumerable<long>)' or 'Params1.set_Item(params IEnumerable<long>, int)' // _ = new Params1()[1]; Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params1()[1]").WithArguments("Params1.this[" + setModifier + "System.Collections.Generic.IEnumerable<long>]", "Params1.get_Item(" + getModifier + "System.Collections.Generic.IEnumerable<long>)", "Params1.set_Item(" + setModifier + "System.Collections.Generic.IEnumerable<long>, int)").WithLocation(6, 13), // (7,13): error CS1545: Property, indexer, or event 'Params2.this[params long[]]' is not supported by the language; try directly calling accessor methods 'Params2.get_Item(params long[])' or 'Params2.set_Item(params long[], int)' // _ = new Params2()[2]; Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params2()[2]").WithArguments("Params2.this[" + setModifier + "long[]]", "Params2.get_Item(" + getModifier + "long[])", "Params2.set_Item(" + setModifier + "long[], int)").WithLocation(7, 13), // (8,9): error CS1545: Property, indexer, or event 'Params1.this[params IEnumerable<long>]' is not supported by the language; try directly calling accessor methods 'Params1.get_Item(params IEnumerable<long>)' or 'Params1.set_Item(params IEnumerable<long>, int)' // new Params1()[1] = 0; Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params1()[1]").WithArguments("Params1.this[" + setModifier + "System.Collections.Generic.IEnumerable<long>]", "Params1.get_Item(" + getModifier + "System.Collections.Generic.IEnumerable<long>)", "Params1.set_Item(" + setModifier + "System.Collections.Generic.IEnumerable<long>, int)").WithLocation(8, 9), // (9,9): error CS1545: Property, indexer, or event 'Params2.this[params long[]]' is not supported by the language; try directly calling accessor methods 'Params2.get_Item(params long[])' or 'Params2.set_Item(params long[], int)' // new Params2()[2] = 0; Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params2()[2]").WithArguments("Params2.this[" + setModifier + "long[]]", "Params2.get_Item(" + getModifier + "long[])", "Params2.set_Item(" + setModifier + "long[], int)").WithLocation(9, 9) ); } [Theory] [CombinatorialData] public void MetadataImport_14_Property(ParamsAttributes parameterType, ParamsAttributes parameterAttributes) { if (parameterAttributes == ParamsAttributes.None || parameterType is not (ParamsAttributes.Array or ParamsAttributes.Collection) || (parameterAttributes & parameterType) == 0) { return; } var attributesString = GetAttributesIL(parameterAttributes); bool isArrayType = parameterType == ParamsAttributes.Array; var typeString = isArrayType ? "int64[]" : "class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>"; // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable<long> or long[] a] // { // [parameterAttributes] get // { System.Console.Write("Test1"); return 0; } // [parameterAttributes] set // { System.Console.Write("Test1"); } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( " + typeString + @" a ) cil managed { " + attributesString + @" .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname instance void set_Item ( " + typeString + @" a, int32 'value' ) cil managed { " + attributesString + @" .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( " + typeString + @" a ) { .get instance int32 Params1::get_Item(" + typeString + @") .set instance void Params1::set_Item(" + typeString + @", int32) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var src = @" class Program { static void Main() { _ = new Params1()[1]; new Params1()[1] = 0; } } "; var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamArray: (parameterAttributes & ParamsAttributes.Array) != 0, isParamCollection: (parameterAttributes & ParamsAttributes.Collection) != 0); CompileAndVerify(comp, expectedOutput: "Test1Test1").VerifyDiagnostics(); } [Theory] [CombinatorialData] public void MetadataImport_15_Property(ParamsAttributes parameterType, ParamsAttributes parameterAttributes) { switch (parameterType, parameterAttributes) { case (ParamsAttributes.Array, ParamsAttributes.Collection): case (ParamsAttributes.Collection, ParamsAttributes.Array): break; default: return; } var attributesString = GetAttributesIL(parameterAttributes); bool isArrayType = parameterType == ParamsAttributes.Array; var typeString = isArrayType ? "int64[]" : "class [mscorlib]System.Collections.Generic.IEnumerable`1<int64>"; // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable<long> or long[] a] // { // [parameterAttributes] get // { System.Console.Write("Test1"); return 0; } // [parameterAttributes] set // { System.Console.Write("Test1"); } // } // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname instance int32 get_Item ( " + typeString + @" a ) cil managed { " + attributesString + @" .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ldc.i4.0 IL_000b: ret } .method public hidebysig specialname instance void set_Item ( " + typeString + @" a, int32 'value' ) cil managed { " + attributesString + @" .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } .property instance int32 Item( " + typeString + @" a ) { .get instance int32 Params1::get_Item(" + typeString + @") .set instance void Params1::set_Item(" + typeString + @", int32) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var src = @" class Program { static void Main() { _ = new Params1()[1]; new Params1()[1] = 0; } } "; var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); var test1 = comp.GetMember<PropertySymbol>("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); VerifyParams(test1, isParamArray: parameterAttributes == ParamsAttributes.Array, isParamCollection: parameterAttributes == ParamsAttributes.Collection); if (isArrayType) { comp.VerifyDiagnostics( // (6,27): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' // _ = new Params1()[1]; Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params long[]").WithLocation(6, 27), // (7,23): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' // new Params1()[1] = 0; Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params long[]").WithLocation(7, 23) ); } else { comp.VerifyDiagnostics( // (6,27): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable<long>' // _ = new Params1()[1]; Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable<long>").WithLocation(6, 27), // (7,23): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable<long>' // new Params1()[1] = 0; Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable<long>").WithLocation(7, 23) ); } } [Fact] public void UsingPatternWithParamsTest() { var source = @" using System.Collections.Generic; ref struct S1 { public void Dispose(params IEnumerable<int> args) { System.Console.Write(""Disposed""); } } class C2 { static void Main() { using (S1 c = new S1()) { } S1 c1b = new S1(); using (c1b) { } } }"; CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "DisposedDisposed").VerifyDiagnostics(); } [Fact] public void UsingPatternWithParamsTest_Foreach() { var source = @" using System.Collections.Generic; ref struct S1 { public void Dispose(params IEnumerable<int> args) { System.Console.Write(""Disposed""); } public int Current => 0; public bool MoveNext() => false; } class C2 { public S1 GetEnumerator() => default; static void Main() { foreach (var i in new C2()) { } } }"; var comp = CreateCompilation(source, options: TestOptions.DebugExe); CompileAndVerify( comp, expectedOutput: "Disposed", verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.FailsILVerify with { ILVerifyMessage = "[GetEnumerator]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x9 }" } : Verification.Passes ).VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var node = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single(); comp.VerifyOperationTree(node, expectedOperationTree: """ IForEachLoopOperation (LoopKind.ForEach, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null) (Syntax: 'foreach (va ... }') Locals: Local_1: System.Int32 i LoopControlVariable: IVariableDeclaratorOperation (Symbol: System.Int32 i) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'var') Initializer: null Collection: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: C2, IsImplicit) (Syntax: 'new C2()') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IObjectCreationOperation (Constructor: C2..ctor()) (OperationKind.ObjectCreation, Type: C2) (Syntax: 'new C2()') Arguments(0) Initializer: null Body: IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') NextVariables(0) """); VerifyFlowGraph(comp, node.Parent.Parent, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { CaptureIds: [0] Block[B1] - Block Predecessors: [B0] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new C2()') Value: IInvocationOperation ( S1 C2.GetEnumerator()) (OperationKind.Invocation, Type: S1, IsImplicit) (Syntax: 'new C2()') Instance Receiver: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: C2, IsImplicit) (Syntax: 'new C2()') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) Operand: IObjectCreationOperation (Constructor: C2..ctor()) (OperationKind.ObjectCreation, Type: C2) (Syntax: 'new C2()') Arguments(0) Initializer: null Arguments(0) Next (Regular) Block[B2] Entering: {R2} {R3} .try {R2, R3} { Block[B2] - Block Predecessors: [B1] [B3] Statements (0) Jump if False (Regular) to Block[B5] IInvocationOperation ( System.Boolean S1.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new C2()') Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S1, IsImplicit) (Syntax: 'new C2()') Arguments(0) Finalizing: {R5} Leaving: {R3} {R2} {R1} Next (Regular) Block[B3] Entering: {R4} .locals {R4} { Locals: [System.Int32 i] Block[B3] - Block Predecessors: [B2] Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var') Left: ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var') Right: IPropertyReferenceOperation: System.Int32 S1.Current { get; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var') Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S1, IsImplicit) (Syntax: 'new C2()') Next (Regular) Block[B2] Leaving: {R4} } } .finally {R5} { Block[B4] - Block Predecessors (0) Statements (1) IInvocationOperation ( void S1.Dispose(params System.Collections.Generic.IEnumerable<System.Int32> args)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'new C2()') Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S1, IsImplicit) (Syntax: 'new C2()') Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: args) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'foreach (va ... }') ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable<System.Int32>, IsImplicit) (Syntax: 'foreach (va ... }') Elements(0) InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Next (StructuredExceptionHandling) Block[null] } } Block[B5] - Exit Predecessors: [B2] Statements (0) """); } [Fact] public void ERR_ExplicitImplParams_01() { var source = @" using System.Collections.Generic; interface I1 { void M1(params IEnumerable<int> args); void M2(IEnumerable<int> args); void M3(params IEnumerable<int> args); } class C2 : I1 { void I1.M1(IEnumerable<int> args) {} void I1.M2(params IEnumerable<int> args) {} void I1.M3(params IEnumerable<int> args) {} } "; CreateCompilation(source).VerifyDiagnostics( // (13,13): error CS0466: 'C2.I1.M2(params IEnumerable<int>)' should not have a params parameter since 'I1.M2(IEnumerable<int>)' does not // void I1.M2(params IEnumerable<int> args) {} Diagnostic(ErrorCode.ERR_ExplicitImplParams, "M2").WithArguments("C2.I1.M2(params System.Collections.Generic.IEnumerable<int>)", "I1.M2(System.Collections.Generic.IEnumerable<int>)").WithLocation(13, 13) ); } [Fact] public void ERR_ExplicitImplParams_02() { // public interface I1 // { // void M1([ParamArrayAttribute] IEnumerable<int> args); // void M2([ParamCollectionAttribute] int[] args); // } var il = @" .class interface public auto ansi abstract beforefieldinit I1 { .method public hidebysig newslot abstract virtual instance void M1 ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> args ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) } .method public hidebysig newslot abstract virtual instance void M2 ( int32[] args ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) } } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } "; var source = @" using System.Collections.Generic; class C1 : I1 { void I1.M1(IEnumerable<int> args) {} void I1.M2(int[] args) {} } class C2 : I1 { void I1.M1(params IEnumerable<int> args) {} void I1.M2(params int[] args) {} } "; CreateCompilationWithIL(source, il).VerifyDiagnostics(); } [Fact] public void EmbedAttribute_01_Constructor() { var src = """ using System.Collections.Generic; class Program { public Program(params IEnumerable<long> x) { } } """; VerifyAttributeEmbedding( src, "Program..ctor", "Program..ctor(params System.Collections.Generic.IEnumerable<System.Int64> x)", // (5,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // public Program(params IEnumerable<long> x) Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable<long> x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 20) ); } private void VerifyAttributeEmbedding(string src, string memberName, string memberDisplay, params DiagnosticDescription[] moduleDiagnostic) { VerifyAttributeEmbedding(src1: src, src2: null, memberName, memberDisplay, moduleDiagnostic); } private void VerifyAttributeEmbedding(string src1, string src2, string memberName, string memberDisplay, params DiagnosticDescription[] moduleDiagnostic) { IEnumerable<MetadataReference> references = src2 is null ? [] : [CreateCompilation(src2).ToMetadataReference()]; var comp = CreateCompilation(src1, references, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)); comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); verify(comp, attributeIsEmbedded: true); var comp1 = CreateCompilation(ParamCollectionAttributeSource, options: TestOptions.ReleaseDll); var comp1Ref = comp1.ToMetadataReference(); var comp2 = CreateCompilation(src1, references: references.Concat([comp1Ref]), options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)); verify(comp2, attributeIsEmbedded: false); Assert.Contains(comp1Ref, comp2.GetUsedAssemblyReferences()); var comp3 = CreateCompilation(src1, references, options: TestOptions.ReleaseModule); comp3.VerifyEmitDiagnostics(moduleDiagnostic); Assert.NotEmpty(moduleDiagnostic); var comp4 = CreateCompilation(src1, references: references.Concat([comp1Ref]), options: TestOptions.ReleaseModule.WithMetadataImportOptions(MetadataImportOptions.All)); verify(comp4, attributeIsEmbedded: false); Assert.Contains(comp1Ref, comp4.GetUsedAssemblyReferences()); const string brokenParamCollectionAttributeSource = @" namespace System.Runtime.CompilerServices { public sealed class ParamCollectionAttribute : Attribute { public ParamCollectionAttribute(int x) { } } } "; var comp5 = CreateCompilation(brokenParamCollectionAttributeSource, options: TestOptions.ReleaseDll); var comp5Ref = comp5.ToMetadataReference(); var comp6 = CreateCompilation(src1, references: references.Concat([comp5Ref]), options: TestOptions.ReleaseDll); comp6.VerifyEmitDiagnostics( // (5,20): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.ParamCollectionAttribute..ctor' // public Program(params IEnumerable<long> x) Diagnostic(ErrorCode.ERR_MissingPredefinedMember, moduleDiagnostic[0].SquiggledText).WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute", ".ctor").WithLocation(moduleDiagnostic[0].LocationLine, moduleDiagnostic[0].LocationCharacter) ); var comp7 = CreateCompilation(src1, references: references.Concat([comp5Ref]), options: TestOptions.ReleaseModule); comp7.VerifyEmitDiagnostics( // (5,20): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.ParamCollectionAttribute..ctor' // public Program(params IEnumerable<long> x) Diagnostic(ErrorCode.ERR_MissingPredefinedMember, moduleDiagnostic[0].SquiggledText).WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute", ".ctor").WithLocation(moduleDiagnostic[0].LocationLine, moduleDiagnostic[0].LocationCharacter) ); void verify(CSharpCompilation comp, bool attributeIsEmbedded) { // We want to test attribute embedding Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); CompileAndVerify( comp, symbolValidator: (m) => { string adjustedMemberName = memberName; string adjustedMemberDisplay = memberDisplay; if (comp.Options.OutputKind == OutputKind.NetModule && memberName.StartsWith("<>")) { adjustedMemberName = adjustedMemberName.Replace("<>", "<" + comp.SourceAssembly.Name + ">"); adjustedMemberDisplay = adjustedMemberDisplay.Replace("<>", "<" + comp.SourceAssembly.Name + ">"); } MethodSymbol member = m.GlobalNamespace.GetMember<MethodSymbol>(adjustedMemberName); AssertEx.Equal(adjustedMemberDisplay, member.ToTestDisplayString()); VerifyParamsAndAttribute(member.Parameters[0], isParamCollection: true); if (member.AssociatedSymbol is PropertySymbol prop) { VerifyParams(prop.Parameters[0], isParamCollection: true); } if (attributeIsEmbedded) { Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); } else if (!m.GlobalNamespace.GetMembers("System").IsEmpty) { Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); } }, verify: comp.Options.OutputKind != OutputKind.NetModule ? Verification.Passes : Verification.Fails with { PEVerifyMessage = "The module was expected to contain an assembly manifest.", ILVerifyMessage = "The format of a DLL or executable being loaded is invalid" } ).VerifyDiagnostics(); } } [Fact] public void EmbedAttribute_02_Delegate() { var src = """ using System.Collections.Generic; delegate void Program(params IEnumerable<long> x); """; VerifyAttributeEmbedding( src, "Program.Invoke", "void Program.Invoke(params System.Collections.Generic.IEnumerable<System.Int64> x)", // (3,23): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // delegate void Program(params IEnumerable<long> x); Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable<long> x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(3, 23) ); } [Fact] public void EmbedAttribute_03_AnonymousDelegate_FromMember() { var src1 = @" class Program { static void Main() { var x1 = Params.Test1; x1(1); } } "; var src2 = @" using System.Collections.Generic; public class Params { static public void Test1(params IEnumerable<long> a) { } } "; VerifyAttributeEmbedding( src1, src2, "<>f__AnonymousDelegate0.Invoke", "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable<System.Int64> arg)", // (6,18): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // var x1 = Params.Test1; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Params.Test1").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(6, 18) ); } [Fact] public void EmbedAttribute_04_AnonymousDelegate_FromLambda() { var src = @" using System.Collections.Generic; class Program { static void Main() { var x1 = (params IEnumerable<long> a) => {}; x1(1); } } "; VerifyAttributeEmbedding( src, "<>f__AnonymousDelegate0.Invoke", "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable<System.Int64> arg)", // (8,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // var x1 = (params IEnumerable<long> a) => {}; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable<long> a").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(8, 19) ); } [Fact] public void EmbedAttribute_05_AnonymousDelegate_FromLambda_ExpressionTree() { var src = @" using System.Collections.Generic; using System.Linq.Expressions; class Program { static void Main() { Test((params IEnumerable<long> a) => {}); } static void Test<T>(Expression<T> e){} } "; CreateCompilation(src).VerifyEmitDiagnostics( // (9,9): error CS0411: The type arguments for method 'Program.Test<T>(Expression<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // Test((params IEnumerable<long> a) => {}); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Program.Test<T>(System.Linq.Expressions.Expression<T>)").WithLocation(9, 9) ); } [Fact] public void EmbedAttribute_06_AnonymousDelegate_FromLocalFunction() { var src = """ using System.Collections.Generic; class Program { static void Main() { var x1 = Test1; x1(1); void Test1(params IEnumerable<long> a) { } } } """; VerifyAttributeEmbedding( src, "<>f__AnonymousDelegate0.Invoke", "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable<System.Int64> arg)", // (11,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // void Test1(params IEnumerable<long> a) { } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable<long> a").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(11, 20) ); } [Fact] public void EmbedAttribute_07_RegularMethod() { var src = """ using System.Collections.Generic; class Program { void Test(params IEnumerable<long> x) { } } """; VerifyAttributeEmbedding( src, "Program.Test", "void Program.Test(params System.Collections.Generic.IEnumerable<System.Int64> x)", // (5,15): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // void Test(params IEnumerable<long> x) Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable<long> x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 15) ); } [Fact] public void EmbedAttribute_08_Operator() { var src = @" using System.Collections.Generic; class Program { public static implicit operator Program(params List<long> x) { return null; } public static Program operator +(Program x, params List<long> y) { return null; } } "; CreateCompilation(src).VerifyEmitDiagnostics( // (6,45): error CS1670: params is not valid in this context // public static implicit operator Program(params List<long> x) Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(6, 45), // (11,49): error CS1670: params is not valid in this context // public static Program operator +(Program x, params List<long> y) Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(11, 49) ); } [Fact] public void EmbedAttribute_09_Property_get() { var src = """ using System.Collections.Generic; class Program { int this[params IEnumerable<long> x] => 0; } """; VerifyAttributeEmbedding( src, "Program.get_Item", "System.Int32 Program.this[params System.Collections.Generic.IEnumerable<System.Int64> x].get", // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // int this[params IEnumerable<long> x] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable<long> x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) ); } [Fact] public void EmbedAttribute_10_Property_get() { var src = """ using System.Collections.Generic; class Program { int this[params IEnumerable<long> x] { get => 0; set {} } } """; VerifyAttributeEmbedding( src, "Program.get_Item", "System.Int32 Program.this[params System.Collections.Generic.IEnumerable<System.Int64> x].get", // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // int this[params IEnumerable<long> x] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable<long> x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) ); } [Fact] public void EmbedAttribute_11_Property_set() { var src = """ using System.Collections.Generic; class Program { int this[params IEnumerable<long> x] {set{}} } """; VerifyAttributeEmbedding( src, "Program.set_Item", "void Program.this[params System.Collections.Generic.IEnumerable<System.Int64> x].set", // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // int this[params IEnumerable<long> x] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable<long> x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) ); } [Fact] public void EmbedAttribute_12_Property_set() { var src = """ using System.Collections.Generic; class Program { int this[params IEnumerable<long> x] {get => 0; set{}} } """; VerifyAttributeEmbedding( src, "Program.set_Item", "void Program.this[params System.Collections.Generic.IEnumerable<System.Int64> x].set", // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported // int this[params IEnumerable<long> x] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable<long> x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) ); } [Fact] public void EmbedAttribute_13_ParamCollectionAttributeNotPortedInNoPia() { var comAssembly = CreateCompilationWithMscorlib40(@" using System; using System.Collections.Generic; using System.Runtime.InteropServices; [assembly: ImportedFromTypeLib(""test.dll"")] [assembly: Guid(""9784f9a1-594a-4351-8f69-0fd2d2df03d3"")] [ComImport()] [Guid(""9784f9a1-594a-4351-8f69-0fd2d2df03d3"")] public interface Test { int Method(params IEnumerable<int> x); }"); // We want to test attribute embedding Assert.Null(comAssembly.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor)); CompileAndVerify(comAssembly, symbolValidator: module => { var type = module.ContainingAssembly.GetTypeByMetadataName("Test"); var method = type.GetMethod("Method"); var parameter = method.Parameters.Single(); VerifyParamsAndAttribute(parameter, isParamCollection: true); }); var code = @" class User { public void M(Test p) { p.Method(0); } }"; var options = TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All); var compilation_CompilationReference = CreateCompilationWithMscorlib40(code, options: options, references: new[] { comAssembly.ToMetadataReference(embedInteropTypes: true) }); CompileAndVerify(compilation_CompilationReference, symbolValidator: symbolValidator); var compilation_BinaryReference = CreateCompilationWithMscorlib40(code, options: options, references: new[] { comAssembly.EmitToImageReference(embedInteropTypes: true) }); CompileAndVerify(compilation_BinaryReference, symbolValidator: symbolValidator); void symbolValidator(ModuleSymbol module) { // Attribute is not embedded if (!module.GlobalNamespace.GetMembers("System").IsEmpty) { Assert.Empty(((NamespaceSymbol)module.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); } var type = module.ContainingAssembly.GetTypeByMetadataName("Test"); var method = type.GetMethod("Method"); var parameter = method.Parameters.Single(); // Attribute is not applied VerifyParamsAndAttribute(parameter, isParamCollection: false); } } [Fact] public void ParamCollectionAttributeNotPortedInNoPia_01() { var comAssembly = CreateCompilationWithMscorlib40(@" using System; using System.Collections.Generic; using System.Runtime.InteropServices; [assembly: ImportedFromTypeLib(""test.dll"")] [assembly: Guid(""9784f9a1-594a-4351-8f69-0fd2d2df03d3"")] [ComImport()] [Guid(""9784f9a1-594a-4351-8f69-0fd2d2df03d3"")] public interface Test { int Method(params IEnumerable<int> x); }" + ParamCollectionAttributeSource); Assert.NotNull(comAssembly.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor)); CompileAndVerify(comAssembly, symbolValidator: module => { var type = module.ContainingAssembly.GetTypeByMetadataName("Test"); var method = type.GetMethod("Method"); var parameter = method.Parameters.Single(); VerifyParamsAndAttribute(parameter, isParamCollection: true); }); var code = @" class User { public void M(Test p) { p.Method(0); } }"; var options = TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All); var compilation_CompilationReference = CreateCompilationWithMscorlib40(code, options: options, references: new[] { comAssembly.ToMetadataReference(embedInteropTypes: true) }); CompileAndVerify(compilation_CompilationReference, symbolValidator: symbolValidator); var compilation_BinaryReference = CreateCompilationWithMscorlib40(code, options: options, references: new[] { comAssembly.EmitToImageReference(embedInteropTypes: true) }); CompileAndVerify(compilation_BinaryReference, symbolValidator: symbolValidator); void symbolValidator(ModuleSymbol module) { // Attribute is not embedded if (!module.GlobalNamespace.GetMembers("System").IsEmpty) { Assert.Empty(((NamespaceSymbol)module.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); } var type = module.ContainingAssembly.GetTypeByMetadataName("Test"); var method = type.GetMethod("Method"); var parameter = method.Parameters.Single(); // Attribute is not applied VerifyParamsAndAttribute(parameter, isParamCollection: false); } } [Fact] public void ParamCollectionAttributeNotPortedInNoPia_02() { var comp1 = CreateCompilationWithMscorlib40(ParamCollectionAttributeSource, options: TestOptions.ReleaseDll); var comp1Ref = comp1.EmitToImageReference(); var comAssembly = CreateCompilationWithMscorlib40(@" using System; using System.Collections.Generic; using System.Runtime.InteropServices; [assembly: ImportedFromTypeLib(""test.dll"")] [assembly: Guid(""9784f9a1-594a-4351-8f69-0fd2d2df03d3"")] [ComImport()] [Guid(""9784f9a1-594a-4351-8f69-0fd2d2df03d3"")] public interface Test { int Method(params IEnumerable<int> x); }", references: [comp1Ref]); Assert.NotNull(comAssembly.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor)); CompileAndVerify(comAssembly, symbolValidator: module => { var type = module.ContainingAssembly.GetTypeByMetadataName("Test"); var method = type.GetMethod("Method"); var parameter = method.Parameters.Single(); VerifyParamsAndAttribute(parameter, isParamCollection: true); }); var code = @" class User { public void M(Test p) { p.Method(0); } }"; var options = TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All); var compilation_CompilationReference = CreateCompilationWithMscorlib40(code, options: options, references: new[] { comp1Ref, comAssembly.ToMetadataReference(embedInteropTypes: true) }); CompileAndVerify(compilation_CompilationReference, symbolValidator: symbolValidator); var compilation_BinaryReference = CreateCompilationWithMscorlib40(code, options: options, references: new[] { comp1Ref, comAssembly.EmitToImageReference(embedInteropTypes: true) }); CompileAndVerify(compilation_BinaryReference, symbolValidator: symbolValidator); void symbolValidator(ModuleSymbol module) { // Attribute is not embedded if (!module.GlobalNamespace.GetMembers("System").IsEmpty) { Assert.Empty(((NamespaceSymbol)module.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); } var type = module.ContainingAssembly.GetTypeByMetadataName("Test"); var method = type.GetMethod("Method"); var parameter = method.Parameters.Single(); // Attribute is not applied VerifyParamsAndAttribute(parameter, isParamCollection: false); } } [Fact] public void ConsumeAcrossAssemblyBoundary_01_Method() { var src1 = """ using System.Collections.Generic; using System.Linq; public class Params { public static void Test(params IEnumerable<long> a) { var array = a.ToArray(); if (array.Length == 0) { System.Console.WriteLine(array.Length); } else { System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); } } } """; var src2 = """ class Program { static void Main() { Params.Test(); Params.Test(1); Params.Test(2, 3); } } """; var comp1 = CreateCompilation(src1); verify(image: true); verify(image: false); void verify(bool image) { var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); CompileAndVerify( comp2, verify: image ? Verification.Passes : Verification.Skipped, expectedOutput: ExpectedOutput(@" 0 1: 1 ... 1 2: 2 ... 3 ")).VerifyDiagnostics(); } } [Theory] [CombinatorialData] public void ConsumeAcrossAssemblyBoundary_02_Property(bool hasSet) { var src1 = """ using System.Collections.Generic; using System.Linq; public class Params { public int this[char c, params IEnumerable<long> a] { get { var array = a.ToArray(); if (array.Length == 0) { System.Console.WriteLine(array.Length); } else { System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); } return 0; } """ + (hasSet ? """ set {} """ : "") + """ } } """; var src2 = """ class Program { static void Main() { var p = new Params(); _ = p['a']; _ = p['b', 1]; _ = p['c', 2, 3]; } } """; var comp1 = CreateCompilation(src1); verify(image: true); verify(image: false); void verify(bool image) { var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); CompileAndVerify( comp2, verify: image ? Verification.Passes : Verification.Skipped, expectedOutput: ExpectedOutput(@" 0 1: 1 ... 1 2: 2 ... 3 ")).VerifyDiagnostics(); } } [Theory] [CombinatorialData] public void ConsumeAcrossAssemblyBoundary_03_Property(bool hasGet) { var src1 = """ using System.Collections.Generic; using System.Linq; public class Params { public int this[char c, params IEnumerable<long> a] { set { var array = a.ToArray(); if (array.Length == 0) { System.Console.WriteLine(array.Length); } else { System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); } } """ + (hasGet ? """ get => 0; """ : "") + """ } } """; var src2 = """ class Program { static void Main() { var p = new Params(); p['a'] = 0; p['b', 1] = 1; p['c', 2, 3] = 2; } } """; var comp1 = CreateCompilation(src1); verify(image: true); verify(image: false); void verify(bool image) { var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); CompileAndVerify( comp2, verify: image ? Verification.Passes : Verification.Skipped, expectedOutput: ExpectedOutput(@" 0 1: 1 ... 1 2: 2 ... 3 ")).VerifyDiagnostics(); } } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/71840")] public void UnsafeContext_01_Constructor() { string source1 = """ using System.Collections; using System.Collections.Generic; public class MyCollectionOfInt : IEnumerable<int> { unsafe public MyCollectionOfInt(void* dummy = null){} public List<int> Array = new List<int>(); IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(int l) => Array.Add(l); } """; var comp1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll); var comp1Ref = comp1.EmitToImageReference(); string source2 = """ class Program { unsafe public static void Test(params MyCollectionOfInt a) { } } """; var comp2 = CreateCompilation(source2, references: [comp1Ref], options: TestOptions.UnsafeDebugDll); comp2.VerifyEmitDiagnostics(); string source3 = """ public class Params { public static void Test(params MyCollectionOfInt a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } } """; var comp3 = CreateCompilation(source3, references: [comp1Ref], options: TestOptions.DebugDll); comp3.VerifyEmitDiagnostics(); string source4 = """ class Program { static void Main() { Params.Test(); Params.Test(1); Params.Test(2, 3); } } """; var comp4 = CreateCompilation(source4, references: [comp1Ref, comp3.ToMetadataReference()], parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe); comp4.VerifyEmitDiagnostics( // (5,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // Params.Test(); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "Params.Test()").WithLocation(5, 9), // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // Params.Test(1); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "Params.Test(1)").WithLocation(6, 9), // (7,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // Params.Test(2, 3); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "Params.Test(2, 3)").WithLocation(7, 9) ); DiagnosticDescription[] expectedPreviewDiagnostics = [ // (5,9): error CS9363: 'MyCollectionOfInt.MyCollectionOfInt(void*)' must be used in an unsafe context because it has pointers in its signature // Params.Test(); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "Params.Test()").WithArguments("MyCollectionOfInt.MyCollectionOfInt(void*)").WithLocation(5, 9), // (6,9): error CS9363: 'MyCollectionOfInt.MyCollectionOfInt(void*)' must be used in an unsafe context because it has pointers in its signature // Params.Test(1); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "Params.Test(1)").WithArguments("MyCollectionOfInt.MyCollectionOfInt(void*)").WithLocation(6, 9), // (7,9): error CS9363: 'MyCollectionOfInt.MyCollectionOfInt(void*)' must be used in an unsafe context because it has pointers in its signature // Params.Test(2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "Params.Test(2, 3)").WithArguments("MyCollectionOfInt.MyCollectionOfInt(void*)").WithLocation(7, 9), ]; comp4 = CreateCompilation(source4, references: [comp1Ref, comp3.ToMetadataReference()], options: TestOptions.ReleaseExe); comp4.VerifyEmitDiagnostics(expectedPreviewDiagnostics); comp4 = CreateCompilation(source4, references: [comp1Ref, comp3.ToMetadataReference()], parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); comp4.VerifyEmitDiagnostics(expectedPreviewDiagnostics); string source5 = """ class Program { static unsafe void Main() { Params.Test(); Params.Test(1); Params.Test(2, 3); } } """; var comp5 = CreateCompilation(source5, references: [comp1Ref, comp3.ToMetadataReference()], options: TestOptions.UnsafeReleaseExe); CompileAndVerify( comp5, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/71840")] public void UnsafeContext_02_Add() { string source1 = """ using System.Collections; using System.Collections.Generic; public class MyCollectionOfInt : IEnumerable<int> { public List<int> Array = new List<int>(); IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; unsafe public void Add(int l, void* dummy = null) => Array.Add(l); } """; var comp1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll); var comp1Ref = comp1.EmitToImageReference(); string source2 = """ class Program { unsafe public static void Test(params MyCollectionOfInt a) { } } """; var comp2 = CreateCompilation(source2, references: [comp1Ref], options: TestOptions.UnsafeDebugDll); comp2.VerifyEmitDiagnostics(); string source3 = """ public class Params { public static void Test(params MyCollectionOfInt a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } } """; var comp3 = CreateCompilation(source3, references: [comp1Ref], options: TestOptions.DebugDll); comp3.VerifyEmitDiagnostics(); string source4 = """ class Program { static void Main() { Params.Test(); Params.Test(1); Params.Test(2, 3); } } """; var comp4 = CreateCompilation(source4, references: [comp1Ref, comp3.ToMetadataReference()], parseOptions: TestOptions.Regular14, options: TestOptions.ReleaseExe); comp4.VerifyEmitDiagnostics( // (6,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // Params.Test(1); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "1").WithLocation(6, 21), // (7,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // Params.Test(2, 3); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "2").WithLocation(7, 21), // (7,24): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // Params.Test(2, 3); Diagnostic(ErrorCode.ERR_UnsafeNeeded, "3").WithLocation(7, 24) ); DiagnosticDescription[] expectedPreviewDiagnostics = [ // (6,21): error CS9363: 'MyCollectionOfInt.Add(int, void*)' must be used in an unsafe context because it has pointers in its signature // Params.Test(1); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "1").WithArguments("MyCollectionOfInt.Add(int, void*)").WithLocation(6, 21), // (7,21): error CS9363: 'MyCollectionOfInt.Add(int, void*)' must be used in an unsafe context because it has pointers in its signature // Params.Test(2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "2").WithArguments("MyCollectionOfInt.Add(int, void*)").WithLocation(7, 21), // (7,24): error CS9363: 'MyCollectionOfInt.Add(int, void*)' must be used in an unsafe context because it has pointers in its signature // Params.Test(2, 3); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "3").WithArguments("MyCollectionOfInt.Add(int, void*)").WithLocation(7, 24), ]; comp4 = CreateCompilation(source4, references: [comp1Ref, comp3.ToMetadataReference()], options: TestOptions.ReleaseExe); comp4.VerifyEmitDiagnostics(expectedPreviewDiagnostics); comp4 = CreateCompilation(source4, references: [comp1Ref, comp3.ToMetadataReference()], parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); comp4.VerifyEmitDiagnostics(expectedPreviewDiagnostics); string source5 = """ class Program { static unsafe void Main() { Params.Test(); Params.Test(1); Params.Test(2, 3); } } """; var comp5 = CreateCompilation(source5, references: [comp1Ref, comp3.ToMetadataReference()], options: TestOptions.UnsafeReleaseExe); CompileAndVerify( comp5, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] public void Cycle_01() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public List<object> Array; public MyCollection(params MyCollection p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l, params MyCollection p) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (20,9): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test(); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(20, 9), // (21,9): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test(1); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(21, 9), // (22,9): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test(2, 3); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 9) ); } [Fact] public void Cycle_02() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public List<object> Array; public MyCollection(params MyCollection p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (20,9): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test(); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(20, 9), // (21,9): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test(1); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(21, 9), // (22,9): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test(2, 3); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 9) ); } [Fact] public void Cycle_03() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public List<object> Array; public MyCollection() { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l, params MyCollection p) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] public void Cycle_04() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public List<object> Array; public MyCollection(params MyCollection p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l, params MyCollection p) => Array.Add(l); } class Program { static void Main() { Test([]); Test([1]); Test([2, 3]); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (20,14): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test([]); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(20, 14), // (21,14): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test([1]); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[1]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(21, 14), // (22,14): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test([2, 3]); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[2, 3]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 14) ); } [Fact] public void Cycle_05() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public List<object> Array; public MyCollection(params MyCollection p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class Program { static void Main() { Test([]); Test([1]); Test([2, 3]); } static void Test(params MyCollection a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (20,14): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test([]); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(20, 14), // (21,14): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test([1]); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[1]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(21, 14), // (22,14): error CS9223: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. // Test([2, 3]); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[2, 3]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 14) ); } [Fact] public void Cycle_06() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public List<object> Array; public MyCollection() { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l, params MyCollection p) => Array.Add(l); } class Program { static void Main() { Test([]); Test([1]); Test([2, 3]); } static void Test(params MyCollection a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] public void Cycle_07() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable { public List<object> Array; public MyCollection(params MyCollection<MyCollection<T>> p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l, params MyCollection<MyCollection<T>> p) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection<int> a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (20,9): error CS9223: Creation of params collection 'MyCollection<int>' results in an infinite chain of invocation of constructor 'MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)'. // Test(); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection<int>", "MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)").WithLocation(20, 9), // (21,9): error CS9223: Creation of params collection 'MyCollection<int>' results in an infinite chain of invocation of constructor 'MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)'. // Test(1); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection<int>", "MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)").WithLocation(21, 9), // (22,9): error CS9223: Creation of params collection 'MyCollection<int>' results in an infinite chain of invocation of constructor 'MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)'. // Test(2, 3); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection<int>", "MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)").WithLocation(22, 9) ); } [Fact] public void Cycle_08() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable { public List<object> Array; public MyCollection() { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l, params MyCollection<MyCollection<T>> p) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection<int> a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] public void Cycle_09() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable { public List<object> Array; public MyCollection(params MyCollection<MyCollection<T>> p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection<int> a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (20,9): error CS9223: Creation of params collection 'MyCollection<int>' results in an infinite chain of invocation of constructor 'MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)'. // Test(); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection<int>", "MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)").WithLocation(20, 9), // (21,9): error CS9223: Creation of params collection 'MyCollection<int>' results in an infinite chain of invocation of constructor 'MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)'. // Test(1); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection<int>", "MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)").WithLocation(21, 9), // (22,9): error CS9223: Creation of params collection 'MyCollection<int>' results in an infinite chain of invocation of constructor 'MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)'. // Test(2, 3); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection<int>", "MyCollection<T>.MyCollection(params MyCollection<MyCollection<T>>)").WithLocation(22, 9) ); } [Fact] public void Cycle_10() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection1 : IEnumerable { public List<object> Array; public MyCollection1(params MyCollection2 p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class MyCollection2 : IEnumerable { public List<object> Array; public MyCollection2(params MyCollection1 p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class MyCollection3 : IEnumerable { public List<object> Array; public MyCollection3(params MyCollection2 p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection3 a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (44,9): error CS9223: Creation of params collection 'MyCollection2' results in an infinite chain of invocation of constructor 'MyCollection2.MyCollection2(params MyCollection1)'. // Test(); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection2", "MyCollection2.MyCollection2(params MyCollection1)").WithLocation(44, 9), // (45,9): error CS9223: Creation of params collection 'MyCollection2' results in an infinite chain of invocation of constructor 'MyCollection2.MyCollection2(params MyCollection1)'. // Test(1); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection2", "MyCollection2.MyCollection2(params MyCollection1)").WithLocation(45, 9), // (46,9): error CS9223: Creation of params collection 'MyCollection2' results in an infinite chain of invocation of constructor 'MyCollection2.MyCollection2(params MyCollection1)'. // Test(2, 3); Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection2", "MyCollection2.MyCollection2(params MyCollection1)").WithLocation(46, 9) ); } [Fact] public void Cycle_11() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection1 : IEnumerable { public List<object> Array; public MyCollection1(params int[] p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class MyCollection2 : IEnumerable { public List<object> Array; public MyCollection2(params MyCollection1 p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class MyCollection3 : IEnumerable { public List<object> Array; public MyCollection3(params MyCollection2 p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection3 a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] public void Cycle_12() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection1 : IEnumerable { public List<object> Array; public MyCollection1() { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class MyCollection2 : IEnumerable { public List<object> Array; public MyCollection2(params MyCollection1 p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class MyCollection3 : IEnumerable { public List<object> Array; public MyCollection3(params MyCollection2 p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection3 a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] public void Cycle_13() { var src = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { public List<object> Array; public MyCollection(params int[] p) { Array = new List<object>(); } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(object l) => Array.Add(l); } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { if (a.Array.Count == 0) { System.Console.WriteLine(a.Array.Count); } else { System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 ").VerifyDiagnostics(); } [Fact] public void Cycle_14_ThroughCollectionBuilderAttribute() { var src = """ #pragma warning disable CS0436 // The type 'CollectionBuilderAttribute' in '' conflicts with the imported type 'CollectionBuilderAttribute' in 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. Using the type defined in ''. using System; using System.Collections.Generic; namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public sealed class CollectionBuilderAttribute : Attribute { public CollectionBuilderAttribute(Type builderType, string methodName, params CollectionBuilderAttribute p) { } public IEnumerator<long> GetEnumerator() => throw null; } public class MyCollectionBuilder { public static CollectionBuilderAttribute Create(ReadOnlySpan<long> items) => throw null; } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params CollectionBuilderAttribute a) { } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (9,6): error CS7036: There is no argument given that corresponds to the required parameter 'p' of 'CollectionBuilderAttribute.CollectionBuilderAttribute(Type, string, params CollectionBuilderAttribute)' // [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))").WithArguments("p", "System.Runtime.CompilerServices.CollectionBuilderAttribute.CollectionBuilderAttribute(System.Type, string, params System.Runtime.CompilerServices.CollectionBuilderAttribute)").WithLocation(9, 6), // (12,80): error CS0225: The params parameter must have a valid collection type // public CollectionBuilderAttribute(Type builderType, string methodName, params CollectionBuilderAttribute p) { } Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(12, 80), // (25,13): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params CollectionBuilderAttribute)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "System.Runtime.CompilerServices.Program.Test(params System.Runtime.CompilerServices.CollectionBuilderAttribute)").WithLocation(25, 13), // (26,18): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Runtime.CompilerServices.CollectionBuilderAttribute' // Test(1); Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Runtime.CompilerServices.CollectionBuilderAttribute").WithLocation(26, 18), // (27,13): error CS1501: No overload for method 'Test' takes 2 arguments // Test(2, 3); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(27, 13), // (30,26): error CS0225: The params parameter must have a valid collection type // static void Test(params CollectionBuilderAttribute a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(30, 26) ); } [Fact] public void Cycle_15_ThroughCollectionBuilderAttribute() { var src = """ #pragma warning disable CS0436 // The type 'CollectionBuilderAttribute' in '' conflicts with the imported type 'CollectionBuilderAttribute' in 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. Using the type defined in ''. using System; using System.Collections.Generic; namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public sealed class CollectionBuilderAttribute : Attribute { public CollectionBuilderAttribute(Type builderType, params CollectionBuilderAttribute p) { } public IEnumerator<string> GetEnumerator() => throw null; } public class MyCollectionBuilder { public static CollectionBuilderAttribute Create(ReadOnlySpan<string> items) => throw null; } class Program { static void Main() { Test(); Test("1"); Test("2", "3"); } static void Test(params CollectionBuilderAttribute a) { } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (9,53): error CS1503: Argument 2: cannot convert from 'string' to 'params System.Runtime.CompilerServices.CollectionBuilderAttribute' // [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] Diagnostic(ErrorCode.ERR_BadArgType, "nameof(MyCollectionBuilder.Create)").WithArguments("2", "string", "params System.Runtime.CompilerServices.CollectionBuilderAttribute").WithLocation(9, 53), // (12,61): error CS0225: The params parameter must have a valid collection type // public CollectionBuilderAttribute(Type builderType, params CollectionBuilderAttribute p) { } Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(12, 61), // (25,13): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params CollectionBuilderAttribute)' // Test(); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "System.Runtime.CompilerServices.Program.Test(params System.Runtime.CompilerServices.CollectionBuilderAttribute)").WithLocation(25, 13), // (26,18): error CS1503: Argument 1: cannot convert from 'string' to 'params System.Runtime.CompilerServices.CollectionBuilderAttribute' // Test("1"); Diagnostic(ErrorCode.ERR_BadArgType, @"""1""").WithArguments("1", "string", "params System.Runtime.CompilerServices.CollectionBuilderAttribute").WithLocation(26, 18), // (27,13): error CS1501: No overload for method 'Test' takes 2 arguments // Test("2", "3"); Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(27, 13), // (30,26): error CS0225: The params parameter must have a valid collection type // static void Test(params CollectionBuilderAttribute a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(30, 26) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_16() { var source = """ using System.Collections; using System.Collections.Generic; public class Base : IEnumerable<object> { public IEnumerator<object> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } public class MyCollection1 : Base { public void Add(params MyCollection1 c) {} } public class C { /*<bind>*/ MyCollection1 M() => [1]; /*</bind>*/ } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (18,27): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection1'. // MyCollection1 M() => [1]; Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "1").WithArguments("MyCollection1").WithLocation(18, 27)); VerifyFlowGraphForTest<MethodDeclarationSyntax>(comp, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '[1]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection1..ctor()) (OperationKind.CollectionExpression, Type: MyCollection1, IsInvalid) (Syntax: '[1]') Elements(1): IInvocationOperation ( void MyCollection1.Add(params MyCollection1 c)) (OperationKind.Invocation, Type: System.Void, IsInvalid, IsImplicit) (Syntax: '1') Instance Receiver: IInvalidOperation (OperationKind.Invalid, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '1') Children(0) Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: c) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: '1') ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '1') Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Block[B2] - Exit Predecessors: [B1] Statements (0) """); VerifyOperationTreeForTest<MethodDeclarationSyntax>(comp, """ IMethodBodyOperation (OperationKind.MethodBody, Type: null, IsInvalid) (Syntax: 'MyCollectio ... M() => [1];') BlockBody: null ExpressionBody: IBlockOperation (1 statements) (OperationKind.Block, Type: null, IsInvalid) (Syntax: '=> [1]') IReturnOperation (OperationKind.Return, Type: null, IsInvalid, IsImplicit) (Syntax: '[1]') ReturnedValue: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '[1]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection1..ctor()) (OperationKind.CollectionExpression, Type: MyCollection1, IsInvalid) (Syntax: '[1]') Elements(1): IInvocationOperation ( void MyCollection1.Add(params MyCollection1 c)) (OperationKind.Invocation, Type: System.Void, IsInvalid, IsImplicit) (Syntax: '1') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '1') Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: c) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: '1') ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '1') Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_17() { var source = """ using System.Collections; using System.Collections.Generic; public class Base : IEnumerable<object> { public IEnumerator<object> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } public class MyCollection1 : Base { public void Add(params MyCollection2 c) {} } public class MyCollection2 : Base { public void Add(params MyCollection1 c) { } } public class C { /*<bind>*/ MyCollection1 M() => [1]; /*</bind>*/ } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (23,27): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection2'. // MyCollection1 M() => [1]; Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "1").WithArguments("MyCollection2").WithLocation(23, 27)); VerifyFlowGraphForTest<MethodDeclarationSyntax>(comp, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '[1]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection1..ctor()) (OperationKind.CollectionExpression, Type: MyCollection1, IsInvalid) (Syntax: '[1]') Elements(1): IInvocationOperation ( void MyCollection2.Add(params MyCollection1 c)) (OperationKind.Invocation, Type: System.Void, IsInvalid, IsImplicit) (Syntax: '1') Instance Receiver: IInvalidOperation (OperationKind.Invalid, Type: MyCollection2, IsInvalid, IsImplicit) (Syntax: '1') Children(0) Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: c) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: '1') ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection1..ctor()) (OperationKind.CollectionExpression, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '1') Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Block[B2] - Exit Predecessors: [B1] Statements (0) """); VerifyOperationTreeForTest<MethodDeclarationSyntax>(comp, """ IMethodBodyOperation (OperationKind.MethodBody, Type: null, IsInvalid) (Syntax: 'MyCollectio ... M() => [1];') BlockBody: null ExpressionBody: IBlockOperation (1 statements) (OperationKind.Block, Type: null, IsInvalid) (Syntax: '=> [1]') IReturnOperation (OperationKind.Return, Type: null, IsInvalid, IsImplicit) (Syntax: '[1]') ReturnedValue: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '[1]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection1..ctor()) (OperationKind.CollectionExpression, Type: MyCollection1, IsInvalid) (Syntax: '[1]') Elements(1): IInvocationOperation ( void MyCollection2.Add(params MyCollection1 c)) (OperationKind.Invocation, Type: System.Void, IsInvalid, IsImplicit) (Syntax: '1') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: MyCollection2, IsInvalid, IsImplicit) (Syntax: '1') Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: c) (OperationKind.Argument, Type: null, IsInvalid, IsImplicit) (Syntax: '1') ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection1..ctor()) (OperationKind.CollectionExpression, Type: MyCollection1, IsInvalid, IsImplicit) (Syntax: '1') Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_18() { var source = """ using System.Collections; using System.Collections.Generic; public class Base : IEnumerable<object> { public IEnumerator<object> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } public class MyCollection1 : Base { public void Add(params MyCollection1 c) {} } public class C { MyCollection1 M() => new() { 1 }; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (17,34): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection1'. // MyCollection1 M() => new() { 1 }; Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "1").WithArguments("MyCollection1").WithLocation(17, 34)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_19() { var source = """ using System.Collections; using System.Collections.Generic; public class Base : IEnumerable<object> { public IEnumerator<object> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } public class MyCollection1 : Base { public void Add(params MyCollection2 c) {} } public class MyCollection2 : Base { public void Add(params int[] c) { } } public class C { /*<bind>*/ MyCollection1 M() => new() { 1 }; /*</bind>*/ } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics(); VerifyFlowGraphForTest<MethodDeclarationSyntax>(comp, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { CaptureIds: [0] Block[B1] - Block Predecessors: [B0] Statements (2) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new() { 1 }') Value: IObjectCreationOperation (Constructor: MyCollection1..ctor()) (OperationKind.ObjectCreation, Type: MyCollection1) (Syntax: 'new() { 1 }') Arguments(0) Initializer: null IInvocationOperation ( void MyCollection1.Add(params MyCollection2 c)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '1') Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: MyCollection1, IsImplicit) (Syntax: 'new() { 1 }') Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: c) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '1') ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection2..ctor()) (OperationKind.CollectionExpression, Type: MyCollection2, IsImplicit) (Syntax: '1') Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection1, IsImplicit) (Syntax: 'new() { 1 }') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (ObjectCreation) Operand: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: MyCollection1, IsImplicit) (Syntax: 'new() { 1 }') Leaving: {R1} } Block[B2] - Exit Predecessors: [B1] Statements (0) """); VerifyOperationTreeForTest<MethodDeclarationSyntax>(comp, """ IMethodBodyOperation (OperationKind.MethodBody, Type: null) (Syntax: 'MyCollectio ... ew() { 1 };') BlockBody: null ExpressionBody: IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '=> new() { 1 }') IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: 'new() { 1 }') ReturnedValue: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection1, IsImplicit) (Syntax: 'new() { 1 }') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IObjectCreationOperation (Constructor: MyCollection1..ctor()) (OperationKind.ObjectCreation, Type: MyCollection1) (Syntax: 'new() { 1 }') Arguments(0) Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: MyCollection1) (Syntax: '{ 1 }') Initializers(1): IInvocationOperation ( void MyCollection1.Add(params MyCollection2 c)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '1') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: MyCollection1, IsImplicit) (Syntax: 'new() { 1 }') Arguments(1): IArgumentOperation (ArgumentKind.ParamCollection, Matching Parameter: c) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '1') ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection2..ctor()) (OperationKind.CollectionExpression, Type: MyCollection2, IsImplicit) (Syntax: '1') Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_20() { var source = """ using System.Collections.Generic; List<List<int>> l; l = [[], [2, 3]]; """; CreateCompilation(source).VerifyEmitDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_21() { var source = """ using System.Collections; using System.Collections.Generic; public class MyCollection1<T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(params MyCollection1<T> c) {} } public class C { MyCollection1<object> M() => [1]; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (13,35): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection1<object>'. // MyCollection1<object> M() => [1]; Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "1").WithArguments("MyCollection1<object>").WithLocation(13, 35)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_22() { var source = """ using System.Collections; using System.Collections.Generic; public class MyCollection1<T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(params MyCollection1<object> c) {} } public class C { MyCollection1<object> M() => [1]; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (13,35): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection1<object>'. // MyCollection1<object> M() => [1]; Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "1").WithArguments("MyCollection1<object>").WithLocation(13, 35)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_23() { var source = """ using System.Collections; using System.Collections.Generic; public class MyCollection1<T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(T x) {} public void Add(params MyCollection1<T> c) {} } public class C { MyCollection1<object> M() => [1]; } """; CreateCompilation(source).VerifyEmitDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_24() { var source = """ using System.Collections; using System.Collections.Generic; public class MyCollection1<T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(params MyCollection1<int> c) {} } public class C { MyCollection1<object> M() => [1]; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (13,35): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection1<int>'. // MyCollection1<object> M() => [1]; Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "1").WithArguments("MyCollection1<int>").WithLocation(13, 35)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_25() { var source = """ using System.Collections; using System.Collections.Generic; public class MyCollection1<T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(params MyCollection1<object> c) {} } public class C { public void M() { MyCollection1<int> c = new() { 1 }; c.Add(2); } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (15,40): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection1<object>'. // MyCollection1<int> c = new() { 1 }; Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "1").WithArguments("MyCollection1<object>").WithLocation(15, 40), // (16,15): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection1<object>'. // c.Add(2); Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "2").WithArguments("MyCollection1<object>").WithLocation(16, 15)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_26() { var source = """ using System.Collections; using System.Collections.Generic; public class MyCollection1 : IEnumerable<object> { public MyCollection1(params MyCollection2 c) {} public IEnumerator<object> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(params MyCollection2 c) {} } public class MyCollection2 : IEnumerable<object> { public IEnumerator<object> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(params MyCollection1 c) {} } public class C { MyCollection1 M() => new() { 1 }; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (21,34): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection2'. // MyCollection1 M() => new() { 1 }; Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "1").WithArguments("MyCollection2").WithLocation(21, 34)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74734")] public void Cycle_27() { var source = """ using System.Collections; using System.Collections.Generic; public class MyCollection1 : IEnumerable<object> { public MyCollection1(params MyCollection2 c) {} public IEnumerator<object> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(params MyCollection2 c) {} } public class MyCollection2 : IEnumerable<object> { public IEnumerator<object> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(params MyCollection1 c) {} } public class C { MyCollection1 M() => [1]; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (21,27): error CS9222: Collection initializer results in an infinite chain of instantiations of collection 'MyCollection2'. // MyCollection1 M() => [1]; Diagnostic(ErrorCode.ERR_CollectionInitializerInfiniteChainOfAddCalls, "1").WithArguments("MyCollection2").WithLocation(21, 27)); } [Fact] public void InvalidParamsTypeInPartialMethod() { var src = """ partial class Program { partial void Test1(params int a); partial void Test1(params int a) { } partial void Test2(int a); partial void Test2(params int a) { } partial void Test3(params int a); partial void Test3(int a) { } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (3,24): error CS0225: The params parameter must have a valid collection type // partial void Test1(params int a); Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(3, 24), // (5,24): error CS0225: The params parameter must have a valid collection type // partial void Test1(params int a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(5, 24), // (11,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test2(params int a) Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test2").WithLocation(11, 18), // (11,24): error CS0225: The params parameter must have a valid collection type // partial void Test2(params int a) Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(11, 24), // (15,24): error CS0225: The params parameter must have a valid collection type // partial void Test3(params int a); Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(15, 24), // (17,18): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter // partial void Test3(int a) Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "Test3").WithLocation(17, 18) ); } [Fact] public void InvalidParamsType() { var src = """ partial class Program { int this[params int a] => a; void Test() { var x = (params int a) => a; local (0); int local(params int a) => a; } } delegate void D(params int a); """; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( // (3,14): error CS0225: The params parameter must have a valid collection type // int this[params int a] => a; Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(3, 14), // (7,18): error CS0225: The params parameter must have a valid collection type // var x = (params int a) => a; Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(7, 18), // (10,19): error CS0225: The params parameter must have a valid collection type // int local(params int a) => a; Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(10, 19), // (14,17): error CS0225: The params parameter must have a valid collection type // delegate void D(params int a); Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(14, 17) ); } [Fact] public void CollectionWithRequiredMember_01() { var src = """ using System.Collections; using System.Collections.Generic; public class MyCollection1 : IEnumerable<long> { IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => throw null; public required int F; } public class MyCollection2 : IEnumerable<long> { IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => throw null; } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection1 a) { } [System.Obsolete] static void Test(MyCollection2 a) { } static void Test2(MyCollection1 a) { } static void Test2() { MyCollection1 b = [1]; Test([2, 3]); Test2([2, 3]); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (24,9): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. // Test(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Test()").WithArguments("MyCollection1.F").WithLocation(24, 9), // (25,9): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. // Test(1); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Test(1)").WithArguments("MyCollection1.F").WithLocation(25, 9), // (26,9): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. // Test(2, 3); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Test(2, 3)").WithArguments("MyCollection1.F").WithLocation(26, 9), // (29,22): error CS9225: Constructor 'MyCollection1.MyCollection1()' leaves required member 'MyCollection1.F' uninitialized. // static void Test(params MyCollection1 a) Diagnostic(ErrorCode.ERR_ParamsCollectionConstructorDoesntInitializeRequiredMember, "params").WithArguments("MyCollection1.MyCollection1()", "MyCollection1.F").WithLocation(29, 22), // (44,27): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. // MyCollection1 b = [1]; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "[1]").WithArguments("MyCollection1.F").WithLocation(44, 27), // (45,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Test(params MyCollection1)' and 'Program.Test(MyCollection2)' // Test([2, 3]); Diagnostic(ErrorCode.ERR_AmbigCall, "Test").WithArguments("Program.Test(params MyCollection1)", "Program.Test(MyCollection2)").WithLocation(45, 9), // (46,15): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. // Test2([2, 3]); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "[2, 3]").WithArguments("MyCollection1.F").WithLocation(46, 15) ); } [Fact] public void CollectionWithRequiredMember_02() { var src = """ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; public class MyCollection : IEnumerable<long> { [SetsRequiredMembers] public MyCollection(){} public List<long> Array = new List<long>(); IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => Array.Add(l); public required int F; } class Program { static void Main() { Test(); Test(1); Test(2, 3); } static void Test(params MyCollection a) { } static void Test2() { Test([2, 3]); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); } [Fact] public void ParamsOverriding_01() { var src = @" using System.Collections.Generic; abstract class C1 { public abstract void Test(params IEnumerable<long> a); } class C2 : C1 { public override void Test(IEnumerable<long> a) {} } "; var comp = CreateCompilation(src); comp.VerifyDiagnostics(); VerifyParams(comp.GetMember<MethodSymbol>("C1.Test").Parameters.Single(), isParamCollection: true); VerifyParams(comp.GetMember<MethodSymbol>("C2.Test").Parameters.Single(), isParamCollection: true); } [Fact] public void ParamsOverriding_02() { var src = @" using System.Collections.Generic; abstract class C1 { public abstract void Test(IEnumerable<long> a); } class C2 : C1 { public override void Test(params IEnumerable<long> a) {} } "; var comp = CreateCompilation(src); comp.VerifyDiagnostics(); VerifyParams(comp.GetMember<MethodSymbol>("C1.Test").Parameters.Single()); VerifyParams(comp.GetMember<MethodSymbol>("C2.Test").Parameters.Single()); } [Fact] public void NullableAnalysis_01() { var src = """ #nullable enable using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<string> { IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw null!; IEnumerator IEnumerable.GetEnumerator() => throw null!; public void Add(string? l) => throw null!; } class Program { static void Test_1(string? x, string y, string? z, string u, string? v, string w, string? s) { Test1(); Test1(x); Test1(y); Test1(z, u, v); Test1(w, s); } static void Test_2(string? x, string y, string? z, string u, string? v, string w, string? s) { Test2(); Test2(x); Test2(y); Test2(z, u, v); Test2(w, s); } static void Test1(params MyCollection paramsParameter) { } static void Test2(params string[] paramsParameter) { } } """; var comp = CreateCompilation(src); comp.VerifyDiagnostics( // (18,15): warning CS8604: Possible null reference argument for parameter 'paramsParameter' in 'void Program.Test1(params MyCollection paramsParameter)'. // Test1(x); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("paramsParameter", "void Program.Test1(params MyCollection paramsParameter)").WithLocation(18, 15), // (20,15): warning CS8604: Possible null reference argument for parameter 'paramsParameter' in 'void Program.Test1(params MyCollection paramsParameter)'. // Test1(z, u, v); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "z").WithArguments("paramsParameter", "void Program.Test1(params MyCollection paramsParameter)").WithLocation(20, 15), // (20,21): warning CS8604: Possible null reference argument for parameter 'paramsParameter' in 'void Program.Test1(params MyCollection paramsParameter)'. // Test1(z, u, v); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "v").WithArguments("paramsParameter", "void Program.Test1(params MyCollection paramsParameter)").WithLocation(20, 21), // (21,18): warning CS8604: Possible null reference argument for parameter 'paramsParameter' in 'void Program.Test1(params MyCollection paramsParameter)'. // Test1(w, s); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("paramsParameter", "void Program.Test1(params MyCollection paramsParameter)").WithLocation(21, 18), // (27,15): warning CS8604: Possible null reference argument for parameter 'paramsParameter' in 'void Program.Test2(params string[] paramsParameter)'. // Test2(x); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("paramsParameter", "void Program.Test2(params string[] paramsParameter)").WithLocation(27, 15), // (29,15): warning CS8604: Possible null reference argument for parameter 'paramsParameter' in 'void Program.Test2(params string[] paramsParameter)'. // Test2(z, u, v); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "z").WithArguments("paramsParameter", "void Program.Test2(params string[] paramsParameter)").WithLocation(29, 15), // (29,21): warning CS8604: Possible null reference argument for parameter 'paramsParameter' in 'void Program.Test2(params string[] paramsParameter)'. // Test2(z, u, v); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "v").WithArguments("paramsParameter", "void Program.Test2(params string[] paramsParameter)").WithLocation(29, 21), // (30,18): warning CS8604: Possible null reference argument for parameter 'paramsParameter' in 'void Program.Test2(params string[] paramsParameter)'. // Test2(w, s); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("paramsParameter", "void Program.Test2(params string[] paramsParameter)").WithLocation(30, 18) ); } [Fact] public void NullableAnalysis_02_GenericInference() { var src = """ #nullable enable using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null!; IEnumerator IEnumerable.GetEnumerator() => throw null!; public void Add(T? l) => throw null!; } class Program { static void Test_1(string? x, string y, string? z, string u, string? v, string w, string? s) { Test1(x) = null; Test1(y) = null; Test1(z, u, v) = null; Test1(w, s) = null; } static void Test_2(string? x, string y, string? z, string u, string? v, string w, string? s) { Test2(x) = null; Test2(y) = null; Test2(z, u, v) = null; Test2(w, s) = null; } static ref T Test1<T>(params MyCollection<T> paramsParameter) { throw null!; } static ref T Test2<T>(params T[] paramsParameter) { throw null!; } } """; var comp = CreateCompilation(src); comp.VerifyDiagnostics( // (18,20): warning CS8625: Cannot convert null literal to non-nullable reference type. // Test1(y) = null; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(18, 20), // (26,20): warning CS8625: Cannot convert null literal to non-nullable reference type. // Test2(y) = null; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(26, 20) ); } [Fact] public void GetAsyncEnumerator_WithParams() { string source = @" using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; class MyCollection : IEnumerable<long> { public MyCollection() { System.Console.Write(""MyCollection ""); } IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => throw null; } class C { public static async Task Main() { await foreach (var i in new C()) { } } public Enumerator GetAsyncEnumerator(params MyCollection x) { System.Console.Write(""GetAsyncEnumerator""); return new Enumerator(); } public sealed class Enumerator { public async Task<bool> MoveNextAsync() { await Task.Yield(); return false; } public int Current { get => throw null; } } }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: "MyCollection GetAsyncEnumerator").VerifyDiagnostics(); } [Fact] public void TestMoveNextAsync_WithParamsParameter() { string source = @" using System.Collections; using System.Collections.Generic; public class MyCollection : IEnumerable<long> { public MyCollection() { System.Console.Write(""MyCollection ""); } IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => throw null; } public class C { public static async System.Threading.Tasks.Task Main() { await foreach (var i in new C()) { } } public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default) { return new Enumerator(); } public sealed class Enumerator { public async System.Threading.Tasks.Task<bool> MoveNextAsync(params MyCollection ok) { System.Console.Write($""MoveNextAsync""); await System.Threading.Tasks.Task.Yield(); return false; } public int Current { get => throw null; } } }"; var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MyCollection MoveNextAsync"); } [Fact] public void PatternBasedDisposal_WithParams() { string source = @" using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; public class MyCollection : IEnumerable<long> { public MyCollection() { System.Console.Write(""MyCollection ""); } IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(long l) => throw null; } class C { public static async Task Main() { await foreach (var i in new C()) { } System.Console.Write(""Done""); } public Enumerator GetAsyncEnumerator() { return new Enumerator(); } public sealed class Enumerator { public async Task<bool> MoveNextAsync() { System.Console.Write(""MoveNextAsync ""); await Task.Yield(); return false; } public int Current { get => throw null; } public async Task DisposeAsync(params MyCollection s) { System.Console.Write(""DisposeAsync ""); await Task.Yield(); } } }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync MyCollection DisposeAsync Done"); } [Fact] public void OperatorWithParams_01() { var src = @" using System.Collections.Generic; class C { static void Test(Program p, List<long> l, long[] a) { p = (Program)l; p = (Program)a; _ = p + l; _ = p + a; } } class Program { public static implicit operator Program(params List<long> x) { return null; } public static Program operator +(Program x, params List<long> y) { return null; } public static implicit operator Program(params long[] x) { return null; } public static Program operator +(Program x, params long[] y) { return null; } } "; CreateCompilation(src).VerifyEmitDiagnostics( // (18,45): error CS1670: params is not valid in this context // public static implicit operator Program(params List<long> x) Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(18, 45), // (23,49): error CS1670: params is not valid in this context // public static Program operator +(Program x, params List<long> y) Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(23, 49), // (28,45): error CS1670: params is not valid in this context // public static implicit operator Program(params long[] x) Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(28, 45), // (33,49): error CS1670: params is not valid in this context // public static Program operator +(Program x, params long[] y) Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(33, 49) ); } [Fact] public void OperatorWithParams_02() { // public class Program // { // public static implicit operator Program(params List<long> x) // { // return null; // } // // public static Program operator +(Program x, params List<long> y) // { // return null; // } // // public static implicit operator Program(params long[] x) // { // return null; // } // // public static Program operator +(Program x, params long[] y) // { // return null; // } // } var ilSource = """ .class public auto ansi beforefieldinit Program extends [mscorlib]System.Object { .method public hidebysig specialname static class Program op_Implicit ( class [mscorlib]System.Collections.Generic.List`1<int64> x ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldnull IL_0001: ret } .method public hidebysig specialname static class Program op_Addition ( class Program x, class [mscorlib]System.Collections.Generic.List`1<int64> y ) cil managed { .param [2] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldnull IL_0001: ret } .method public hidebysig specialname static class Program op_Implicit ( int64[] x ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldnull IL_0001: ret } .method public hidebysig specialname static class Program op_Addition ( class Program x, int64[] y ) cil managed { .param [2] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldnull IL_0001: ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } } .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } """; var src = @" using System.Collections.Generic; class C { static void Test(Program p, List<long> l, long[] a) { p = (Program)l; p = (Program)a; _ = p + l; _ = p + a; } } "; CreateCompilationWithIL(src, ilSource).VerifyEmitDiagnostics( // (8,13): error CS0030: Cannot convert type 'System.Collections.Generic.List<long>' to 'Program' // p = (Program)l; Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Program)l").WithArguments("System.Collections.Generic.List<long>", "Program").WithLocation(8, 13), // (9,13): error CS0030: Cannot convert type 'long[]' to 'Program' // p = (Program)a; Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Program)a").WithArguments("long[]", "Program").WithLocation(9, 13), // (11,13): error CS0019: Operator '+' cannot be applied to operands of type 'Program' and 'List<long>' // _ = p + l; Diagnostic(ErrorCode.ERR_BadBinaryOps, "p + l").WithArguments("+", "Program", "System.Collections.Generic.List<long>").WithLocation(11, 13), // (12,13): error CS0019: Operator '+' cannot be applied to operands of type 'Program' and 'long[]' // _ = p + a; Diagnostic(ErrorCode.ERR_BadBinaryOps, "p + a").WithArguments("+", "Program", "long[]").WithLocation(12, 13) ); } [Fact] public void ExtensionMethodWithParams_01() { var src = @" using System.Collections.Generic; class C { static void Test(IEnumerable<long> l, long[] a) { l.M1(); a.M2(); } } static class Ext { public static void M1(this params IEnumerable<long> x) { } public static void M2(this params long[] x) { } } "; CreateCompilation(src).VerifyEmitDiagnostics( // (15,32): error CS1104: A parameter array cannot be used with 'this' modifier on an extension method // public static void M1(this params IEnumerable<long> x) Diagnostic(ErrorCode.ERR_BadParamModThis, "params").WithLocation(15, 32), // (19,32): error CS1104: A parameter array cannot be used with 'this' modifier on an extension method // public static void M2(this params long[] x) Diagnostic(ErrorCode.ERR_BadParamModThis, "params").WithLocation(19, 32) ); } [Fact] public void ExtensionMethodWithParams_02() { // static class Ext // { // public static void M1(this params IEnumerable<long> x) // { // } // // public static void M2(this params long[] x) // { // } // } var ilSource = """ .class public auto ansi abstract sealed beforefieldinit Ext extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .method public hidebysig static void M1 ( class [mscorlib]System.Collections.Generic.IEnumerable`1<int64> x ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ret } .method public hidebysig static void M2 ( int64[] x ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ret } } .class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Attribute::.ctor() IL_0006: ret } } """; var src = @" using System.Collections.Generic; class C { static void Test(IEnumerable<long> l, long[] a) { l.M1(); a.M2(); } } "; CreateCompilationWithIL(src, ilSource).VerifyEmitDiagnostics( // (8,11): error CS1061: 'IEnumerable<long>' does not contain a definition for 'M1' and no accessible extension method 'M1' accepting a first argument of type 'IEnumerable<long>' could be found (are you missing a using directive or an assembly reference?) // l.M1(); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M1").WithArguments("System.Collections.Generic.IEnumerable<long>", "M1").WithLocation(8, 11), // (9,11): error CS1061: 'long[]' does not contain a definition for 'M2' and no accessible extension method 'M2' accepting a first argument of type 'long[]' could be found (are you missing a using directive or an assembly reference?) // a.M2(); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M2").WithArguments("long[]", "M2").WithLocation(9, 11) ); } [Fact] public void VarargWithParams_01() { var src = @" using System.Collections.Generic; class C1 { C1(params IEnumerable<long> x, __arglist) { } C1(params long[] x, __arglist) { } static void M1(params IEnumerable<long> x, __arglist) { } static void M2(params long[] x, __arglist) { } } class C2 { C2(IEnumerable<long> x, __arglist) { } C2(long[] x, __arglist) { } static void M3(IEnumerable<long> x, __arglist) { } static void M4(long[] x, __arglist) { } } "; CreateCompilation(src).VerifyEmitDiagnostics( // (6,5): error CS0224: A method with vararg cannot be generic, be in a generic type, or have a params parameter // C1(params IEnumerable<long> x, __arglist) Diagnostic(ErrorCode.ERR_BadVarargs, "C1").WithLocation(6, 5), // (6,8): error CS0231: A params parameter must be the last parameter in a parameter list // C1(params IEnumerable<long> x, __arglist) Diagnostic(ErrorCode.ERR_ParamsLast, "params IEnumerable<long> x").WithLocation(6, 8), // (10,5): error CS0224: A method with vararg cannot be generic, be in a generic type, or have a params parameter // C1(params long[] x, __arglist) Diagnostic(ErrorCode.ERR_BadVarargs, "C1").WithLocation(10, 5), // (10,8): error CS0231: A params parameter must be the last parameter in a parameter list // C1(params long[] x, __arglist) Diagnostic(ErrorCode.ERR_ParamsLast, "params long[] x").WithLocation(10, 8), // (14,17): error CS0224: A method with vararg cannot be generic, be in a generic type, or have a params parameter // static void M1(params IEnumerable<long> x, __arglist) Diagnostic(ErrorCode.ERR_BadVarargs, "M1").WithLocation(14, 17), // (14,20): error CS0231: A params parameter must be the last parameter in a parameter list // static void M1(params IEnumerable<long> x, __arglist) Diagnostic(ErrorCode.ERR_ParamsLast, "params IEnumerable<long> x").WithLocation(14, 20), // (18,17): error CS0224: A method with vararg cannot be generic, be in a generic type, or have a params parameter // static void M2(params long[] x, __arglist) Diagnostic(ErrorCode.ERR_BadVarargs, "M2").WithLocation(18, 17), // (18,20): error CS0231: A params parameter must be the last parameter in a parameter list // static void M2(params long[] x, __arglist) Diagnostic(ErrorCode.ERR_ParamsLast, "params long[] x").WithLocation(18, 20) ); } [Fact] public void ParamsNotLast_01() { var src = @" using System.Collections.Generic; class C1 { C1(params IEnumerable<long> x, int y) { } static void M1(params IEnumerable<long> x, int y) { } int this[params IEnumerable<long> x, int y] => 0; } delegate void D1(params IEnumerable<long> x, int y); class C2 { C2(IEnumerable<long> x, int y) { } static void M3(IEnumerable<long> x, int y) { } int this[IEnumerable<long> x, int y] => 0; } delegate void D2(IEnumerable<long> x, int y); "; CreateCompilation(src).VerifyDiagnostics( // (6,8): error CS0231: A params parameter must be the last parameter in a parameter list // C1(params IEnumerable<long> x, int y) Diagnostic(ErrorCode.ERR_ParamsLast, "params IEnumerable<long> x").WithLocation(6, 8), // (10,20): error CS0231: A params parameter must be the last parameter in a parameter list // static void M1(params IEnumerable<long> x, int y) Diagnostic(ErrorCode.ERR_ParamsLast, "params IEnumerable<long> x").WithLocation(10, 20), // (14,14): error CS0231: A params parameter must be the last parameter in a parameter list // int this[params IEnumerable<long> x, int y] => 0; Diagnostic(ErrorCode.ERR_ParamsLast, "params IEnumerable<long> x").WithLocation(14, 14), // (17,18): error CS0231: A params parameter must be the last parameter in a parameter list // delegate void D1(params IEnumerable<long> x, int y); Diagnostic(ErrorCode.ERR_ParamsLast, "params IEnumerable<long> x").WithLocation(17, 18) ); } [Fact] public void ExplicitParamCollectionAttribute() { var source = """ var lam = ([System.Runtime.CompilerServices.ParamCollectionAttribute] int[] xs) => xs.Length; """; CreateCompilation([source, ParamCollectionAttributeSource]).VerifyDiagnostics( // (1,13): error CS0674: Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. // var lam = ([System.Runtime.CompilerServices.ParamCollectionAttribute] int[] xs) => xs.Length; Diagnostic(ErrorCode.ERR_ExplicitParamArrayOrCollection, "System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(1, 13) ); } [Theory] [InlineData("in")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("out")] public void ParamsCantBeWithModifier_01(string modifier) { var src = @" using System.Collections.Generic; class C1 { static void M1(params " + modifier + @" IEnumerable<long> x) { throw null; } } "; if (modifier == "ref readonly") { modifier = "ref"; } CreateCompilation(src).VerifyDiagnostics( // (6,27): error CS1611: The params parameter cannot be declared as in // static void M1(params in IEnumerable<long> x) Diagnostic(ErrorCode.ERR_ParamsCantBeWithModifier, modifier).WithArguments(modifier).WithLocation(6, 27) ); } [Theory] [InlineData("in")] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("out")] public void ParamsCantBeWithModifier_02(string modifier) { var src = @" using System.Collections.Generic; class C1 { static void M1(" + modifier + @" params IEnumerable<long> x) { throw null; } } "; if (modifier == "ref readonly") { modifier = "ref"; } CreateCompilation(src).VerifyDiagnostics( // (7,1): error CS8328: The parameter modifier 'params' cannot be used with 'in' // params IEnumerable<long> x) Diagnostic(ErrorCode.ERR_BadParameterModifiers, "params").WithArguments("params", modifier).WithLocation(7, 1) ); } [Fact] public void IllegalParams_01() { var src = @" using System.Collections.Generic; var lam = delegate (params IEnumerable<long> xs) { return 0; }; "; CreateCompilation(src).VerifyDiagnostics( // (4,21): error CS1670: params is not valid in this context // var lam = delegate (params IEnumerable<long> xs) Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(4, 21) ); } [Fact] public void ParamsWithDefault_01() { var src = @" using System.Collections.Generic; class C1 { static void M1(params IEnumerable<long> x = null) { } } "; CreateCompilation(src).VerifyDiagnostics( // (6,20): error CS1751: Cannot specify a default value for a parameter collection // static void M1(params IEnumerable<long> x = null) Diagnostic(ErrorCode.ERR_DefaultValueForParamsParameter, "params").WithLocation(6, 20) ); } [Fact] public void NoOverloadingOnParams_01() { var src = @" using System.Collections.Generic; class C1 { static void M1(params IEnumerable<long> x) { } static void M1(IEnumerable<long> x) { } static void M2(IEnumerable<long> x) { } static void M2(params IEnumerable<long> x) { } } "; CreateCompilation(src).VerifyDiagnostics( // (9,17): error CS0111: Type 'C1' already defines a member called 'M1' with the same parameter types // static void M1(IEnumerable<long> x) Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "C1").WithLocation(9, 17), // (16,17): error CS0111: Type 'C1' already defines a member called 'M2' with the same parameter types // static void M2(params IEnumerable<long> x) Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M2").WithArguments("M2", "C1").WithLocation(16, 17) ); } [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] [Fact] public void AddMethod_Derived_01() { string source = """ using System.Collections; using System.Collections.Generic; class Element { } class ElementCollection : IEnumerable { private readonly List<object> _list = new(); public IEnumerator GetEnumerator() => _list.GetEnumerator(); public void Add(Element element) { _list.Add(element); } } class Program { static void Main() { Test(new Element(), null); } static void Test(params ElementCollection c) { c.Report(); } } """; CompileAndVerify([source, CollectionExpressionTests.s_collectionExtensions], expectedOutput: "[Element, null], "); } [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] [Fact] public void AddMethod_Derived_02() { string source = """ using System.Collections; using System.Collections.Generic; class Base { } class Element : Base { } class ElementCollection : IEnumerable<Base> { private readonly List<Base> _list = new(); public IEnumerator<Base> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public void Add(Element element) { _list.Add(element); } } class Program { static void Main() { Test(new Element(), null); } static void Test(params ElementCollection c) { c.Report(); } } """; CompileAndVerify([source, CollectionExpressionTests.s_collectionExtensions], expectedOutput: "[Element, null], "); } [WorkItem("https://github.com/dotnet/roslyn/issues/71240")] [Fact] public void AddMethod_Derived_03() { string sourceA = """ using System.Collections; using System.Collections.Generic; class Sample<T> : IEnumerable<object[]> { private readonly List<object[]> _list = new(); IEnumerator<object[]> IEnumerable<object[]>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); public void Add(T t) { if (t is object[] o) _list.Add(o); } } """; string sourceB1 = """ class Program { static void Main() { Test(["a"], ["b"], ["c"]); } static void Test(params Sample<string[]> s) { s.Report(); } } """; CompileAndVerify([sourceA, sourceB1, CollectionExpressionTests.s_collectionExtensions], expectedOutput: "[[a], [b], [c]], "); string sourceB2 = """ class Program { static void Main() { Test("a", null); } static void Test(params Sample<string> s) { } } """; var comp = CreateCompilation([sourceA, sourceB2]); comp.VerifyEmitDiagnostics( // (5,14): error CS1503: Argument 1: cannot convert from 'string' to 'object[]' // Test("a", null); Diagnostic(ErrorCode.ERR_BadArgType, @"""a""").WithArguments("1", "string", "object[]").WithLocation(5, 14) ); } [Fact] public void AddMethod_Generic_02() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable { private readonly List<T> _list = new(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); public void Add<U>(T t) { _list.Add(t); } } class Program { static void Test(params MyCollection<object> z) { } static void Main() { int x = 1; Test(x); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (12,22): error CS9215: Collection expression type 'MyCollection<object>' must have an instance or extension method 'Add' that can be called with a single argument. // static void Test(params MyCollection<object> z) Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "params MyCollection<object> z").WithArguments("MyCollection<object>").WithLocation(12, 22), // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection<object>' // Test(x); Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "params MyCollection<object>").WithLocation(19, 14) ); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] public void ParameterTypeSpecificity_01() { string source = """ using System; namespace OverloadResolutionRepro { public class C { public void Method<S>(params Func<Bar, S>[] projections) => Console.Write(1); public void Method<S>(params Func<Bar, Wrapper<S>>[] projections) => Console.Write(2); } public class Bar { public Wrapper<int> WrappedValue { get; set; } = new Wrapper<int>(); } public struct Wrapper<TValue> { } public class EntryPoint { static void Main() { new C().Method(x => x.WrappedValue); } } } """; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] public void ParameterTypeSpecificity_02() { string source = """ using System; namespace OverloadResolutionRepro { public class C<S> { public C(params Func<Bar, S>[] projections) => Console.Write(1); public C(params Func<Bar, Wrapper<int>>[] projections) => Console.Write(2); } public class Bar { public Wrapper<int> WrappedValue { get; set; } = new Wrapper<int>(); } public struct Wrapper<TValue> { } public class EntryPoint { static void Main() { new C<Wrapper<int>>(x => x.WrappedValue); } } } """; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] public void ParameterTypeSpecificity_03() { string source = """ using System; using System.Collections.Generic; namespace OverloadResolutionRepro { public class C { public void Method<S>(params IEnumerable<Func<Bar, S>> projections) => Console.Write(1); public void Method<S>(params IEnumerable<Func<Bar, Wrapper<S>>> projections) => Console.Write(2); } public class Bar { public Wrapper<int> WrappedValue { get; set; } = new Wrapper<int>(); } public struct Wrapper<TValue> { } public class EntryPoint { static void Main() { new C().Method(x => x.WrappedValue); } } } """; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); } [Fact] public void AttributeFromSDK() { var source = """ using System; C.M(1, 2, 3); class C { public static void M(params Span<int> span) { foreach (var item in span) Console.Write(item); } } """; var verifier = CompileAndVerify( source, targetFramework: TargetFramework.Net80, symbolValidator: verify8, verify: Verification.Skipped, expectedOutput: ExpectedOutput("123")); verifier.VerifyDiagnostics(); void verify8(ModuleSymbol module) { // attribute is embedded. Assert.NotNull(module.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); } verifier = CompileAndVerify( source, targetFramework: TargetFramework.Net90, symbolValidator: verify9, verify: Verification.Skipped, expectedOutput: ExpectedOutput("123")); verifier.VerifyDiagnostics(); void verify9(ModuleSymbol module) { // attribute is not embedded. Assert.Empty(module.GlobalNamespace.GetMembers("System")); } } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/73743")] public void ParamsSpanInExpression_01() { string source = """ class Program { public static void Test() { System.Linq.Expressions.Expression<System.Action<string>> e = (s) => M(s, s, s); } static void M(params string[] p) {} static void M(params System.Span<string> p) {} } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( // (5,78): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'Span'. // System.Linq.Expressions.Expression<System.Action<string>> e = (s) => M(s, s, s); Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "M(s, s, s)").WithArguments("Span").WithLocation(5, 78), // (5,78): error CS9226: An expression tree may not contain an expanded form of non-array params collection parameter. // System.Linq.Expressions.Expression<System.Action<string>> e = (s) => M(s, s, s); Diagnostic(ErrorCode.ERR_ParamsCollectionExpressionTree, "M(s, s, s)").WithLocation(5, 78) ); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/73743")] public void ParamsSpanInExpression_02() { string source = """ class Program { public static void Test() { System.Linq.Expressions.Expression<System.Action<string>> e = (s) => M(s, s, s); } static void M(params System.Span<string> p) {} } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( // (5,78): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'Span'. // System.Linq.Expressions.Expression<System.Action<string>> e = (s) => M(s, s, s); Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "M(s, s, s)").WithArguments("Span").WithLocation(5, 78), // (5,78): error CS9226: An expression tree may not contain an expanded form of non-array params collection parameter. // System.Linq.Expressions.Expression<System.Action<string>> e = (s) => M(s, s, s); Diagnostic(ErrorCode.ERR_ParamsCollectionExpressionTree, "M(s, s, s)").WithLocation(5, 78) ); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/74163")] public void StringInterpolation_01() { string source1 = """ class Program { public static void Test1() { System.Linq.Expressions.Expression<System.Func<string, string>> e = (s) => $"{s} {2} {s} {4}"; } public static void Test2() { System.Linq.Expressions.Expression<System.Func<string, System.FormattableString>> e = (s) => $"{s} {2} {s} {4}"; } } """; string core = """ namespace System { public class Object {} public class ValueType {} public abstract partial class Enum {} public struct Void {} public struct Boolean {} public struct Byte {} public struct Int16 {} public struct Int32 {} public struct Int64 {} public struct IntPtr {} public partial class Exception {} public class String { public static string Format(string format, params object[] args) => null; public static string Format(string format, params ReadOnlySpan<object> args) => null; } public abstract class FormattableString {} public abstract partial class Attribute {} public sealed class ParamArrayAttribute : Attribute {} public enum AttributeTargets { Assembly = 1, Module = 2, Class = 4, Struct = 8, Enum = 16, Constructor = 32, Method = 64, Property = 128, Field = 256, Event = 512, Interface = 1024, Parameter = 2048, Delegate = 4096, ReturnValue = 8192, GenericParameter = 16384, All = 32767 } public sealed class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) { } internal AttributeUsageAttribute(AttributeTargets validOn, bool allowMultiple, bool inherited) { } public AttributeTargets ValidOn {get; set;} public bool AllowMultiple {get; set;} public bool Inherited {get; set;} } public abstract partial class Delegate {} public abstract partial class MulticastDelegate : Delegate {} public delegate TResult Func<in T, out TResult>(T arg); public interface IDisposable { void Dispose(); } public partial struct Nullable<T> where T : struct {} public unsafe partial struct RuntimeTypeHandle {} public unsafe partial struct RuntimeMethodHandle {} public abstract partial class Type { public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null; } public ref struct ReadOnlySpan<T> { public ReadOnlySpan(T[] array) { } public ReadOnlySpan(T[] array, int start, int length) { } public unsafe ReadOnlySpan(void* pointer, int length) { } } } namespace System.Collections { public interface IEnumerator { object Current { get; } bool MoveNext(); void Reset(); } public interface IEnumerable { IEnumerator GetEnumerator(); } } namespace System.Collections.Generic { public interface IEnumerator<out T> : IEnumerator, IDisposable { new T Current { get; } } public interface IEnumerable<out T> : IEnumerable { new IEnumerator<T> GetEnumerator(); } } namespace System.Reflection { public abstract unsafe partial class MethodBase { public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle) => null; public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) => null; } public abstract partial class MethodInfo : MethodBase {} public abstract partial class ConstructorInfo : MethodBase {} } namespace System.Linq.Expressions { using System.Collections.Generic; using System.Reflection; public partial class Expression { public static ParameterExpression Parameter(Type type) => null; public static ParameterExpression Parameter(Type type, string name) => null; public static ConstantExpression Constant(object value) => null; public static ConstantExpression Constant(object value, Type type) => null; public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters) => null; public static NewArrayExpression NewArrayInit(Type type, params Expression[] initializers) => null; public static NewExpression New(ConstructorInfo constructor, IEnumerable<Expression> arguments) => null; public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments) => null; public static UnaryExpression Convert(Expression expression, Type type) => null; } public abstract class LambdaExpression : Expression {} public class Expression<TDelegate> : LambdaExpression {} public class ParameterExpression : Expression {} public class ConstantExpression : Expression {} public class NewArrayExpression : Expression {} public class NewExpression : Expression {} public class MethodCallExpression : Expression {} public class UnaryExpression : Expression {} } namespace System.Runtime.CompilerServices { public sealed class InlineArrayAttribute : Attribute { public InlineArrayAttribute(int length) { } } public sealed class IsReadOnlyAttribute : Attribute {} public static class FormattableStringFactory { public static FormattableString Create(string format, params object[] arguments) => null; public static FormattableString Create(string format, params ReadOnlySpan<object> arguments) => null; } public static unsafe partial class Unsafe { public static ref TTo As<TFrom, TTo>(ref TFrom source) => throw null; public static ref T AsRef<T>(scoped ref readonly T source) => throw null; public static ref T Add<T>(ref T source, int elementOffset) => throw null; } } namespace System.Runtime.InteropServices { public static partial class MemoryMarshal { public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref readonly T reference, int length) => default; } } """; var comp = CreateEmptyCompilation([source1, core], options: TestOptions.ReleaseDll.WithAllowUnsafe(true)); var verifier = CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1) ); verifier.VerifyIL("Program.Test1", @" { // Code size 198 (0xc6) .maxstack 11 .locals init (System.Linq.Expressions.ParameterExpression V_0) IL_0000: ldtoken ""string"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""s"" IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" IL_0014: stloc.0 IL_0015: ldnull IL_0016: ldtoken ""string string.Format(string, params object[])"" IL_001b: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_0020: castclass ""System.Reflection.MethodInfo"" IL_0025: ldc.i4.2 IL_0026: newarr ""System.Linq.Expressions.Expression"" IL_002b: dup IL_002c: ldc.i4.0 IL_002d: ldstr ""{0} {1} {2} {3}"" IL_0032: ldtoken ""string"" IL_0037: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_003c: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_0041: stelem.ref IL_0042: dup IL_0043: ldc.i4.1 IL_0044: ldtoken ""object"" IL_0049: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_004e: ldc.i4.4 IL_004f: newarr ""System.Linq.Expressions.Expression"" IL_0054: dup IL_0055: ldc.i4.0 IL_0056: ldloc.0 IL_0057: stelem.ref IL_0058: dup IL_0059: ldc.i4.1 IL_005a: ldc.i4.2 IL_005b: box ""int"" IL_0060: ldtoken ""int"" IL_0065: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_006a: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_006f: ldtoken ""object"" IL_0074: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0079: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" IL_007e: stelem.ref IL_007f: dup IL_0080: ldc.i4.2 IL_0081: ldloc.0 IL_0082: stelem.ref IL_0083: dup IL_0084: ldc.i4.3 IL_0085: ldc.i4.4 IL_0086: box ""int"" IL_008b: ldtoken ""int"" IL_0090: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0095: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_009a: ldtoken ""object"" IL_009f: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_00a4: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" IL_00a9: stelem.ref IL_00aa: call ""System.Linq.Expressions.NewArrayExpression System.Linq.Expressions.Expression.NewArrayInit(System.Type, params System.Linq.Expressions.Expression[])"" IL_00af: stelem.ref IL_00b0: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" IL_00b5: ldc.i4.1 IL_00b6: newarr ""System.Linq.Expressions.ParameterExpression"" IL_00bb: dup IL_00bc: ldc.i4.0 IL_00bd: ldloc.0 IL_00be: stelem.ref IL_00bf: call ""System.Linq.Expressions.Expression<System.Func<string, string>> System.Linq.Expressions.Expression.Lambda<System.Func<string, string>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" IL_00c4: pop IL_00c5: ret } "); verifier.VerifyIL("Program.Test2", @" { // Code size 198 (0xc6) .maxstack 11 .locals init (System.Linq.Expressions.ParameterExpression V_0) IL_0000: ldtoken ""string"" IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_000a: ldstr ""s"" IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" IL_0014: stloc.0 IL_0015: ldnull IL_0016: ldtoken ""System.FormattableString System.Runtime.CompilerServices.FormattableStringFactory.Create(string, params object[])"" IL_001b: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" IL_0020: castclass ""System.Reflection.MethodInfo"" IL_0025: ldc.i4.2 IL_0026: newarr ""System.Linq.Expressions.Expression"" IL_002b: dup IL_002c: ldc.i4.0 IL_002d: ldstr ""{0} {1} {2} {3}"" IL_0032: ldtoken ""string"" IL_0037: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_003c: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_0041: stelem.ref IL_0042: dup IL_0043: ldc.i4.1 IL_0044: ldtoken ""object"" IL_0049: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_004e: ldc.i4.4 IL_004f: newarr ""System.Linq.Expressions.Expression"" IL_0054: dup IL_0055: ldc.i4.0 IL_0056: ldloc.0 IL_0057: stelem.ref IL_0058: dup IL_0059: ldc.i4.1 IL_005a: ldc.i4.2 IL_005b: box ""int"" IL_0060: ldtoken ""int"" IL_0065: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_006a: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_006f: ldtoken ""object"" IL_0074: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0079: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" IL_007e: stelem.ref IL_007f: dup IL_0080: ldc.i4.2 IL_0081: ldloc.0 IL_0082: stelem.ref IL_0083: dup IL_0084: ldc.i4.3 IL_0085: ldc.i4.4 IL_0086: box ""int"" IL_008b: ldtoken ""int"" IL_0090: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_0095: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" IL_009a: ldtoken ""object"" IL_009f: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" IL_00a4: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" IL_00a9: stelem.ref IL_00aa: call ""System.Linq.Expressions.NewArrayExpression System.Linq.Expressions.Expression.NewArrayInit(System.Type, params System.Linq.Expressions.Expression[])"" IL_00af: stelem.ref IL_00b0: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" IL_00b5: ldc.i4.1 IL_00b6: newarr ""System.Linq.Expressions.ParameterExpression"" IL_00bb: dup IL_00bc: ldc.i4.0 IL_00bd: ldloc.0 IL_00be: stelem.ref IL_00bf: call ""System.Linq.Expressions.Expression<System.Func<string, System.FormattableString>> System.Linq.Expressions.Expression.Lambda<System.Func<string, System.FormattableString>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" IL_00c4: pop IL_00c5: ret } "); string source2 = """ class Program { public static string Test1(string s) => $"{s} {2} {s} {4}"; public static System.FormattableString Test2(string s) => $"{s} {2} {s} {4}"; } """; comp = CreateEmptyCompilation([source2, core], options: TestOptions.ReleaseDll.WithAllowUnsafe(true)); verifier = CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1) ); verifier.VerifyIL("Program.Test1", @" { // Code size 77 (0x4d) .maxstack 3 .locals init (<>y__InlineArray4<object> V_0) IL_0000: ldstr ""{0} {1} {2} {3}"" IL_0005: ldloca.s V_0 IL_0007: initobj ""<>y__InlineArray4<object>"" IL_000d: ldloca.s V_0 IL_000f: ldc.i4.0 IL_0010: call ""ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray4<object>, object>(ref <>y__InlineArray4<object>, int)"" IL_0015: ldarg.0 IL_0016: stind.ref IL_0017: ldloca.s V_0 IL_0019: ldc.i4.1 IL_001a: call ""ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray4<object>, object>(ref <>y__InlineArray4<object>, int)"" IL_001f: ldc.i4.2 IL_0020: box ""int"" IL_0025: stind.ref IL_0026: ldloca.s V_0 IL_0028: ldc.i4.2 IL_0029: call ""ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray4<object>, object>(ref <>y__InlineArray4<object>, int)"" IL_002e: ldarg.0 IL_002f: stind.ref IL_0030: ldloca.s V_0 IL_0032: ldc.i4.3 IL_0033: call ""ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray4<object>, object>(ref <>y__InlineArray4<object>, int)"" IL_0038: ldc.i4.4 IL_0039: box ""int"" IL_003e: stind.ref IL_003f: ldloca.s V_0 IL_0041: ldc.i4.4 IL_0042: call ""System.ReadOnlySpan<object> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray4<object>, object>(in <>y__InlineArray4<object>, int)"" IL_0047: call ""string string.Format(string, params System.ReadOnlySpan<object>)"" IL_004c: ret } "); verifier.VerifyIL("Program.Test2", @" { // Code size 77 (0x4d) .maxstack 3 .locals init (<>y__InlineArray4<object> V_0) IL_0000: ldstr ""{0} {1} {2} {3}"" IL_0005: ldloca.s V_0 IL_0007: initobj ""<>y__InlineArray4<object>"" IL_000d: ldloca.s V_0 IL_000f: ldc.i4.0 IL_0010: call ""ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray4<object>, object>(ref <>y__InlineArray4<object>, int)"" IL_0015: ldarg.0 IL_0016: stind.ref IL_0017: ldloca.s V_0 IL_0019: ldc.i4.1 IL_001a: call ""ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray4<object>, object>(ref <>y__InlineArray4<object>, int)"" IL_001f: ldc.i4.2 IL_0020: box ""int"" IL_0025: stind.ref IL_0026: ldloca.s V_0 IL_0028: ldc.i4.2 IL_0029: call ""ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray4<object>, object>(ref <>y__InlineArray4<object>, int)"" IL_002e: ldarg.0 IL_002f: stind.ref IL_0030: ldloca.s V_0 IL_0032: ldc.i4.3 IL_0033: call ""ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray4<object>, object>(ref <>y__InlineArray4<object>, int)"" IL_0038: ldc.i4.4 IL_0039: box ""int"" IL_003e: stind.ref IL_003f: ldloca.s V_0 IL_0041: ldc.i4.4 IL_0042: call ""System.ReadOnlySpan<object> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray4<object>, object>(in <>y__InlineArray4<object>, int)"" IL_0047: call ""System.FormattableString System.Runtime.CompilerServices.FormattableStringFactory.Create(string, params System.ReadOnlySpan<object>)"" IL_004c: ret } "); } [Fact] public void ScopedParams() { var source = """ using System.Collections; using System.Collections.Generic; ref struct S : IEnumerable<int> { public void Add(int i) { } public IEnumerator<int> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } static class C { public static void M(scoped params S x) { } } """; CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( // (12,33): error CS9348: The 'params' modifier cannot immediately follow the 'scoped' modifier. // public static void M(scoped params S x) Diagnostic(ErrorCode.ERR_InvalidModifierAfterScoped, "params").WithArguments("params").WithLocation(12, 33)); } [Fact] public void ParamsScoped() { var source = """ using System.Collections; using System.Collections.Generic; ref struct S : IEnumerable<int> { public void Add(int i) { } public IEnumerator<int> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } static class C { public static void M(params scoped S x) { } } """; CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); } } }