|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public class UserDefinedCompoundAssignmentOperatorsTests : CSharpTestBase
{
private static Verification VerifyOnMonoOrCoreClr
{
get
{
return ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped;
}
}
[Theory]
[CombinatorialData]
public void Increment_001([CombinatorialValues("++", "--")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public void operator" + op + @"() {}
public void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net60);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net60);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.Regular13, targetFramework: TargetFramework.Net60);
comp.VerifyDiagnostics(
// (3,25): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// public void operator++() {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(3, 25),
// (4,33): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// public void operator checked++() {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(4, 33)
);
validate(comp.SourceModule);
comp = CreateCompilation(source, targetFramework: TargetFramework.Net60);
comp.VerifyDiagnostics(
// (3,25): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute..ctor'
// public void operator++() {}
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, op).WithArguments("System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute", ".ctor").WithLocation(3, 25),
// (4,33): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute..ctor'
// public void operator checked++() {}
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, op).WithArguments("System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute", ".ctor").WithLocation(4, 33)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.Equal(typeKeyword == "interface", m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Empty(m.GetAttributes());
}
}
[Theory]
[CombinatorialData]
public void Increment_001_NotInStaticClass([CombinatorialValues("++", "--")] string op)
{
var source = @"
static class C1
{
public void operator" + op + @"() {}
public void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,25): error CS0715: 'C1.operator ++()': static classes cannot contain user-defined operators
// public void operator++() {}
Diagnostic(ErrorCode.ERR_OperatorInStaticClass, op).WithArguments("C1.operator " + op + @"()").WithLocation(4, 25),
// (5,33): error CS0715: 'C1.operator checked ++()': static classes cannot contain user-defined operators
// public void operator checked++() {}
Diagnostic(ErrorCode.ERR_OperatorInStaticClass, op).WithArguments("C1.operator checked " + op + @"()").WithLocation(5, 33)
);
}
[Theory]
[CombinatorialData]
public void Increment_002_MustBePublic([CombinatorialValues("++", "--")] string op, bool structure, bool isChecked)
{
var source =
(structure ? "struct" : "class") + @" C1
{
void operator " + (isChecked ? "checked " : "") + op + @"() {}
" + (isChecked ? "public void operator " + op + @"() {}" : "") + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (3,19): error CS9308: User-defined operator 'C1.operator ++()' must be declared public
// void operator ++() {}
Diagnostic(ErrorCode.ERR_OperatorsMustBePublic, op).WithArguments("C1.operator " + (isChecked ? "checked " : "") + op + @"()").WithLocation(3, 19 + (isChecked ? 8 : 0))
);
}
[Theory]
[CombinatorialData]
public void Increment_002_MustBePublic_ExplicitAccessibility(
[CombinatorialValues("++", "--")] string op,
[CombinatorialValues("struct", "class", "interface")] string typeKeyword,
[CombinatorialValues("private", "internal", "protected", "internal protected", "private protected")] string accessibility,
bool isChecked)
{
var source =
typeKeyword + @" C1
{
" + accessibility + @"
void operator " + (isChecked ? "checked " : "") + op + @"() {}
" + (isChecked ? "public void operator " + op + @"() {}" : "") + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net60);
comp.VerifyDiagnostics(
// (4,19): error CS9308: User-defined operator 'C1.operator ++()' must be declared public
// void operator ++() {}
Diagnostic(ErrorCode.ERR_OperatorsMustBePublic, op).WithArguments("C1.operator " + (isChecked ? "checked " : "") + op + @"()").WithLocation(4, 19 + (isChecked ? 8 : 0))
);
}
[Theory]
[CombinatorialData]
public void Increment_003_MustReturnVoid([CombinatorialValues("++", "--")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public C1 operator " + op + @"() => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,23): error CS9310: The return type for this operator must be void
// public C1 operator ++() => throw null;
Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, op).WithLocation(3, 24)
);
}
[Theory]
[CombinatorialData]
public void Increment_004_MustReturnVoid_Checked([CombinatorialValues("++", "--")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public C1 operator checked " + op + @"() => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,32): error CS9310: The return type for this operator must be void
// public C1 operator checked ++() => throw null;
Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, op).WithLocation(3, 32),
// (3,32): error CS9025: The operator 'C1.operator checked ++()' requires a matching non-checked version of the operator to also be defined
// public C1 operator checked ++() => throw null;
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "()").WithLocation(3, 32)
);
}
[Theory]
[CombinatorialData]
public void Increment_005_WrongNumberOfParameters([CombinatorialValues("++", "--")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source1 = @"
public " + typeKeyword + @" C1
{
#line 3
public void operator" + op + @"(C1 x) {}
public void operator" + op + @"(C1 x, C1 y) {}
public void operator" + op + @"(C1 x, C1 y, C1 z) {}
}
";
var comp1 = CreateCompilation(source1, targetFramework: TargetFramework.Net90);
comp1.VerifyDiagnostics(
// (3,25): error CS0558: User-defined operator 'C1.operator ++(C1)' must be declared static and public
// public void operator++(C1 x) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBeStaticAndPublic, op).WithArguments("C1.operator " + op + "(C1)").WithLocation(3, 25),
// (3,25): error CS0448: The return type for ++ or -- operator must match the parameter type or be derived from the parameter type
// public void operator++(C1 x) {}
Diagnostic(ErrorCode.ERR_BadIncDecRetType, op).WithLocation(3, 25),
// (4,25): error CS1020: Overloadable binary operator expected
// public void operator++(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, op).WithLocation(4, 25),
// (4,25): error CS0558: User-defined operator 'C1.operator ++(C1, C1)' must be declared static and public
// public void operator++(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBeStaticAndPublic, op).WithArguments("C1.operator " + op + "(C1, C1)").WithLocation(4, 25),
// (5,25): error CS1535: Overloaded unary operator '++' takes one parameter
// public void operator++(C1 x, C1 y, C1 z) {}
Diagnostic(ErrorCode.ERR_BadUnOpArgs, op).WithArguments(op).WithLocation(5, 25),
// (5,25): error CS0558: User-defined operator 'C1.operator ++(C1, C1, C1)' must be declared static and public
// public void operator++(C1 x, C1 y, C1 z) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBeStaticAndPublic, op).WithArguments("C1.operator " + op + "(C1, C1, C1)").WithLocation(5, 25)
);
if (typeKeyword == "interface")
{
var source2 = @"
class C2 : C1
{
void C1.operator" + op + @"(C1 x) {}
void C1.operator" + op + @"(C1 x, C1 y) {}
void C1.operator" + op + @"(C1 x, C1 y, C1 z) {}
}
";
var comp2 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net90);
comp2.VerifyDiagnostics(
// (4,21): error CS8930: Explicit implementation of a user-defined operator 'C2.operator ++(C1)' must be declared static
// void C1.operator++(C1 x) {}
Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("C2.operator " + op + "(C1)").WithLocation(4, 21),
// (4,21): error CS0539: 'C2.operator ++(C1)' in explicit interface declaration is not found among members of the interface that can be implemented
// void C1.operator++(C1 x) {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C2.operator " + op + "(C1)").WithLocation(4, 21),
// (5,21): error CS1020: Overloadable binary operator expected
// void C1.operator++(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, op).WithLocation(5, 21),
// (5,21): error CS8930: Explicit implementation of a user-defined operator 'C2.operator ++(C1, C1)' must be declared static
// void C1.operator++(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("C2.operator " + op + "(C1, C1)").WithLocation(5, 21),
// (5,21): error CS0539: 'C2.operator ++(C1, C1)' in explicit interface declaration is not found among members of the interface that can be implemented
// void C1.operator++(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C2.operator " + op + "(C1, C1)").WithLocation(5, 21),
// (6,21): error CS1535: Overloaded unary operator '++' takes one parameter
// void C1.operator++(C1 x, C1 y, C1 z) {}
Diagnostic(ErrorCode.ERR_BadUnOpArgs, op).WithArguments(op).WithLocation(6, 21),
// (6,21): error CS8930: Explicit implementation of a user-defined operator 'C2.operator ++(C1, C1, C1)' must be declared static
// void C1.operator++(C1 x, C1 y, C1 z) {}
Diagnostic(ErrorCode.ERR_ExplicitImplementationOfOperatorsMustBeStatic, op).WithArguments("C2.operator " + op + "(C1, C1, C1)").WithLocation(6, 21),
// (6,21): error CS0539: 'C2.operator ++(C1, C1, C1)' in explicit interface declaration is not found among members of the interface that can be implemented
// void C1.operator++(C1 x, C1 y, C1 z) {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C2.operator " + op + "(C1, C1, C1)").WithLocation(6, 21)
);
}
}
[Theory]
[CombinatorialData]
public void Increment_006_WrongNumberOfParameters_Checked([CombinatorialValues("++", "--")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public void operator checked " + op + @"(C1 x) {}
public void operator checked " + op + @"(C1 x, C1 y) {}
public void operator checked " + op + @"(C1 x, C1 y, C1 z) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,34): error CS0558: User-defined operator 'C1.operator checked ++(C1)' must be declared static and public
// public void operator checked ++(C1 x) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBeStaticAndPublic, op).WithArguments("C1.operator checked " + op + "(C1)").WithLocation(3, 34),
// (3,34): error CS0448: The return type for ++ or -- operator must match the parameter type or be derived from the parameter type
// public void operator checked ++(C1 x) {}
Diagnostic(ErrorCode.ERR_BadIncDecRetType, op).WithLocation(3, 34),
// (3,34): error CS9025: The operator 'C1.operator checked ++(C1)' requires a matching non-checked version of the operator to also be defined
// public void operator checked ++(C1 x) {}
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "(C1)").WithLocation(3, 34),
// (4,34): error CS1020: Overloadable binary operator expected
// public void operator checked ++(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, op).WithLocation(4, 34),
// (4,34): error CS0558: User-defined operator 'C1.operator checked ++(C1, C1)' must be declared static and public
// public void operator checked ++(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBeStaticAndPublic, op).WithArguments("C1.operator checked " + op + "(C1, C1)").WithLocation(4, 34),
// (4,34): error CS9025: The operator 'C1.operator checked ++(C1, C1)' requires a matching non-checked version of the operator to also be defined
// public void operator checked ++(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "(C1, C1)").WithLocation(4, 34),
// (5,34): error CS1535: Overloaded unary operator '++' takes one parameter
// public void operator checked ++(C1 x, C1 y, C1 z) {}
Diagnostic(ErrorCode.ERR_BadUnOpArgs, op).WithArguments(op).WithLocation(5, 34),
// (5,34): error CS0558: User-defined operator 'C1.operator checked ++(C1, C1, C1)' must be declared static and public
// public void operator checked ++(C1 x, C1 y, C1 z) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBeStaticAndPublic, op).WithArguments("C1.operator checked " + op + "(C1, C1, C1)").WithLocation(5, 34),
// (5,34): error CS9025: The operator 'C1.operator checked ++(C1, C1, C1)' requires a matching non-checked version of the operator to also be defined
// public void operator checked ++(C1 x, C1 y, C1 z) {}
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "(C1, C1, C1)").WithLocation(5, 34)
);
}
[Theory]
[CombinatorialData]
public void Increment_007_WrongNumberOfParameters_ForStatic([CombinatorialValues("++", "--")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public static C1 operator" + op + @"() => throw null;
public static C1 operator" + op + @"(C1 x) => throw null;
public static C1 operator" + op + @"(C1 x, C1 y) => throw null;
public static C1 operator" + op + @"(C1 x, C1 y, C1 z) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,30): error CS0106: The modifier 'static' is not valid for this item
// public static C1 operator++() => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(3, 30),
// (3,30): error CS9310: The return type for this operator must be void
// public static C1 operator++() => throw null;
Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, op).WithLocation(3, 30),
// (5,30): error CS1020: Overloadable binary operator expected
// public static C1 operator++(C1 x, C1 y) => throw null;
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, op).WithLocation(5, 30),
// (6,30): error CS1535: Overloaded unary operator '++' takes one parameter
// public static C1 operator++(C1 x, C1 y, C1 z) => throw null;
Diagnostic(ErrorCode.ERR_BadUnOpArgs, op).WithArguments(op).WithLocation(6, 30)
);
}
[Theory]
[CombinatorialData]
public void Increment_008_WrongNumberOfParameters_ForStatic_Checked([CombinatorialValues("++", "--")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public static C1 operator checked " + op + @"() => throw null;
public static C1 operator checked " + op + @"(C1 x) => throw null;
public static C1 operator " + op + @"(C1 x) => throw null;
public static C1 operator checked " + op + @"(C1 x, C1 y) => throw null;
public static C1 operator checked " + op + @"(C1 x, C1 y, C1 z) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,39): error CS0106: The modifier 'static' is not valid for this item
// public static C1 operator checked ++() => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(3, 39),
// (3,39): error CS9310: The return type for this operator must be void
// public static C1 operator checked ++() => throw null;
Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, op).WithLocation(3, 39),
// (3,39): error CS9025: The operator 'C1.operator checked ++()' requires a matching non-checked version of the operator to also be defined
// public static C1 operator checked ++() => throw null;
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "()").WithLocation(3, 39),
// (6,39): error CS1020: Overloadable binary operator expected
// public static C1 operator checked ++(C1 x, C1 y) => throw null;
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, op).WithLocation(6, 39),
// (6,39): error CS9025: The operator 'C1.operator checked ++(C1, C1)' requires a matching non-checked version of the operator to also be defined
// public static C1 operator checked ++(C1 x, C1 y) => throw null;
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "(C1, C1)").WithLocation(6, 39),
// (7,39): error CS1535: Overloaded unary operator '++' takes one parameter
// public static C1 operator checked ++(C1 x, C1 y, C1 z) => throw null;
Diagnostic(ErrorCode.ERR_BadUnOpArgs, op).WithArguments(op).WithLocation(7, 39),
// (7,39): error CS9025: The operator 'C1.operator checked ++(C1, C1, C1)' requires a matching non-checked version of the operator to also be defined
// public static C1 operator checked ++(C1 x, C1 y, C1 z) => throw null;
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "(C1, C1, C1)").WithLocation(7, 39)
);
}
[Theory]
[CombinatorialData]
public void Increment_009_AbstractAllowedInClassAndInterface([CombinatorialValues("++", "--")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public abstract void operator" + op + @"();
public abstract void operator checked" + op + @"();
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
comp = CreateCompilation(["class C2 : C1 {}", source, CompilerFeatureRequiredAttribute]);
if (typeKeyword == "interface")
{
comp.VerifyDiagnostics(
// (1,12): error CS0535: 'C2' does not implement interface member 'C1.operator ++()'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "C1").WithArguments("C2", "C1.operator " + op + @"()").WithLocation(1, 12),
// (1,12): error CS0535: 'C2' does not implement interface member 'C1.operator checked ++()'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "C1").WithArguments("C2", "C1.operator checked " + op + @"()").WithLocation(1, 12)
);
}
else
{
comp.VerifyDiagnostics(
// (1,7): error CS0534: 'C2' does not implement inherited abstract member 'C1.operator checked ++()'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C2").WithArguments("C2", "C1.operator checked " + op + @"()").WithLocation(1, 7),
// (1,7): error CS0534: 'C2' does not implement inherited abstract member 'C1.operator ++()'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C2").WithArguments("C2", "C1.operator " + op + @"()").WithLocation(1, 7)
);
}
}
[Theory]
[CombinatorialData]
public void Increment_010_AbstractIsOptionalInInterface([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface C1
{
public void operator" + op + @"();
public void operator checked" + op + @"();
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void Increment_011_AbstractCanBeImplementedInInterface([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
interface I3 : I1
{
void I1.operator " + op + @"() {}
}
interface I4 : I2
{
void I2.operator checked " + op + @"() {}
}
class C : I3, I4
{}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType<MethodSymbol>().Single(),
"void I1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName) + "()");
validateOp(
m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType<MethodSymbol>().Single(),
"void I2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) + "()");
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
}
[Theory]
[CombinatorialData]
public void Increment_012_AbstractCanBeImplementedExplicitlyInClassAndStruct([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source1 = @"
public interface I1
{
void operator " + op + @"();
}
public interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
";
var source2 =
typeKeyword + @" C3 : I1
{
void I1.operator " + op + @"() {}
}
" + typeKeyword + @" C4 : I2
{
void I2.operator checked " + op + @"() {}
}
";
var comp = CreateCompilation(source1 + source2, targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
var comp1 = CreateCompilation(source1, targetFramework: TargetFramework.Net90);
var comp2 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net90, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(
// (3,22): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(3, 22),
// (8,30): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// void I2.operator checked ++() {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(8, 30)
);
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()).Single(),
"void I1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName) + "()");
validateOp(
m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()).Single(),
"void I2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) + "()");
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
}
[Theory]
[CombinatorialData]
public void Increment_013_AbstractCanBeImplementedImplicitlyInClassAndStruct([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
" + typeKeyword + @" C3 : I1
{
public void operator " + op + @"() {}
}
" + typeKeyword + @" C4 : I2
{
public void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType<MethodSymbol>().
Where(m => m.Name == (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)).Single());
validateOp(m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType<MethodSymbol>().
Where(m => m.Name == (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)).Single());
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
if (m is PEMethodSymbol)
{
Assert.True(m.IsMetadataVirtual());
Assert.True(m.IsMetadataFinal);
}
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void Increment_014_AbstractAllowedOnExplicitImplementationInInterface([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"() {}
}
interface I2
{
void operator checked " + op + @"() {}
sealed void operator " + op + @"() {}
}
interface I3 : I1
{
abstract void I1.operator " + op + @"();
}
interface I4 : I2
{
abstract void I2.operator checked " + op + @"();
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType<MethodSymbol>().Single(),
"void I1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName) + "()");
validateOp(
m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType<MethodSymbol>().Single(),
"void I2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) + "()");
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.True(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
comp = CreateCompilation(["class C1 : I3, I4 {}", source], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (1,12): error CS0535: 'C1' does not implement interface member 'I1.operator ++()'
// class C1 : I3, I4 {}
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I3").WithArguments("C1", "I1.operator " + op + @"()").WithLocation(1, 12),
// (1,16): error CS0535: 'C1' does not implement interface member 'I2.operator checked ++()'
// class C1 : I3, I4 {}
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I4").WithArguments("C1", "I2.operator checked " + op + @"()").WithLocation(1, 16)
);
}
[Theory]
[CombinatorialData]
public void Increment_015_AbstractCannotHaveBody([CombinatorialValues("++", "--")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public abstract void operator" + op + @"() {}
public abstract void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,34): error CS0500: 'C1.operator ++()' cannot declare a body because it is marked abstract
// public abstract void operator++() {}
Diagnostic(ErrorCode.ERR_AbstractHasBody, op).WithArguments("C1.operator " + op + @"()").WithLocation(3, 34),
// (4,42): error CS0500: 'C1.operator checked ++()' cannot declare a body because it is marked abstract
// public abstract void operator checked++() {}
Diagnostic(ErrorCode.ERR_AbstractHasBody, op).WithArguments("C1.operator checked " + op + @"()").WithLocation(4, 42)
);
}
[Theory]
[CombinatorialData]
public void Increment_016_AbstractExplicitImplementationCannotHaveBody([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator" + op + @"();
void operator checked" + op + @"();
}
interface I2 : I1
{
abstract void I1.operator" + op + @"() {}
abstract void I1.operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (10,29): error CS0500: 'I2.I1.operator ++()' cannot declare a body because it is marked abstract
// abstract void I1.operator++() {}
Diagnostic(ErrorCode.ERR_AbstractHasBody, op).WithArguments("I2.I1.operator " + op + @"()").WithLocation(10, 29),
// (11,37): error CS0500: 'I2.I1.operator checked ++()' cannot declare a body because it is marked abstract
// abstract void I1.operator checked++() {}
Diagnostic(ErrorCode.ERR_AbstractHasBody, op).WithArguments("I2.I1.operator checked " + op + @"()").WithLocation(11, 37)
);
}
[Theory]
[CombinatorialData]
public void Increment_017_AbstractNotAllowedInNonAbstractClass([CombinatorialValues("++", "--")] string op, [CombinatorialValues("sealed", "")] string typeModifier)
{
var source = @"
" + typeModifier + @" class C1
{
public abstract void operator" + op + @"();
}
" + typeModifier + @" class C2
{
public abstract void operator checked " + op + @"();
public void operator " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,34): error CS0513: 'C1.operator ++()' is abstract but it is contained in non-abstract type 'C1'
// public abstract void operator++();
Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, op).WithArguments("C1.operator " + op + @"()", "C1").WithLocation(4, 34),
// (9,43): error CS0513: 'C2.operator checked ++()' is abstract but it is contained in non-abstract type 'C2'
// public abstract void operator checked ++();
Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, op).WithArguments("C2.operator checked " + op + @"()", "C2").WithLocation(9, 43)
);
}
[Theory]
[CombinatorialData]
public void Increment_018_AbstractNotAllowedInClass_OnStatic([CombinatorialValues("++", "--")] string op)
{
var source = @"
abstract class C1
{
public static abstract C1 operator" + op + @"(C1 x) => throw null;
}
abstract class C2
{
public static abstract C2 operator checked " + op + @"(C2 x) => throw null;
public static C2 operator " + op + @"(C2 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,39): error CS0106: The modifier 'abstract' is not valid for this item
// public static abstract C1 operator++(C1 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(4, 39),
// (9,48): error CS0106: The modifier 'abstract' is not valid for this item
// public static abstract C2 operator checked ++(C2 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(9, 48)
);
}
[Theory]
[CombinatorialData]
public void Increment_019_AbstractNotAllowedInStruct([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct C1
{
public abstract void operator" + op + @"() {}
}
struct C2
{
public abstract void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
struct C3
{
public static abstract C3 operator" + op + @"(C3 x) => throw null;
}
struct C4
{
public static abstract C4 operator checked " + op + @"(C4 x) => throw null;
public static C4 operator " + op + @"(C4 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,34): error CS0106: The modifier 'abstract' is not valid for this item
// public abstract void operator++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(4, 34),
// (9,43): error CS0106: The modifier 'abstract' is not valid for this item
// public abstract void operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(9, 43),
// (15,39): error CS0106: The modifier 'abstract' is not valid for this item
// public static abstract C3 operator++(C3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(15, 39),
// (20,48): error CS0106: The modifier 'abstract' is not valid for this item
// public static abstract C4 operator checked ++(C4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(20, 48)
);
}
[Theory]
[CombinatorialData]
public void Increment_020_AbstractNotAllowedOnExplicitImplementationInClassAndStruct([CombinatorialValues("++", "--")] string op, [CombinatorialValues("abstract class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
" + typeKeyword + @" C3 : I1
{
abstract void I1.operator " + op + @"() {}
}
" + typeKeyword + @" C4 : I2
{
abstract void I2.operator checked " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,31): error CS0106: The modifier 'abstract' is not valid for this item
// abstract void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(15, 31),
// (20,39): error CS0106: The modifier 'abstract' is not valid for this item
// abstract void I2.operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(20, 39)
);
}
[Theory]
[CombinatorialData]
public void Increment_021_VirtualAllowedInClassAndInterface([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public virtual void operator" + op + @"() {}
public virtual void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void Increment_022_VirtualIsOptionalInInterface([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface C1
{
void operator" + op + @"() {}
void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void Increment_023_VirtualCanBeImplementedInInterface([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
virtual void operator " + op + @"() {}
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
interface I3 : I1
{
void I1.operator " + op + @"() {}
}
interface I4 : I2
{
void I2.operator checked " + op + @"() {}
}
class C : I3, I4
{}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType<MethodSymbol>().Single(),
"void I1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName) + "()");
validateOp(
m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType<MethodSymbol>().Single(),
"void I2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) + "()");
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
}
[Theory]
[CombinatorialData]
public void Increment_024_VirtualCanBeImplementedExplicitlyInClassAndStruct([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
virtual void operator " + op + @"() {}
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
" + typeKeyword + @" C3 : I1
{
void I1.operator " + op + @"() {}
}
" + typeKeyword + @" C4 : I2
{
void I2.operator checked " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()).Single(),
"void I1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName) + "()");
validateOp(
m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()).Single(),
"void I2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) + "()");
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
}
[Theory]
[CombinatorialData]
public void Increment_025_VirtualCanBeImplementedImplicitlyInClassAndStruct([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
virtual void operator " + op + @"() {}
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
" + typeKeyword + @" C3 : I1
{
public void operator " + op + @"() {}
}
" + typeKeyword + @" C4 : I2
{
public void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType<MethodSymbol>().
Where(m => m.Name == (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)).Single());
validateOp(m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType<MethodSymbol>().
Where(m => m.Name == (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)).Single());
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
if (m is PEMethodSymbol)
{
Assert.True(m.IsMetadataVirtual());
Assert.True(m.IsMetadataFinal);
}
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void Increment_026_VirtualMustHaveBody([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public virtual void operator" + op + @"();
public virtual void operator checked" + op + @"();
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,33): error CS0501: 'C1.operator ++()' must declare a body because it is not marked abstract, extern, or partial
// public virtual void operator++();
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("C1.operator " + op + @"()").WithLocation(3, 33),
// (4,41): error CS0501: 'C1.operator checked ++()' must declare a body because it is not marked abstract, extern, or partial
// public virtual void operator checked++();
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("C1.operator checked " + op + @"()").WithLocation(4, 41)
);
}
[Theory]
[CombinatorialData]
public void Increment_027_VirtualNotAllowedInSealedClass([CombinatorialValues("++", "--")] string op)
{
var source = @"
sealed class C1
{
public virtual void operator" + op + @"() {}
}
sealed class C2
{
public virtual void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,33): error CS0549: 'C1.operator ++()' is a new virtual member in sealed type 'C1'
// public virtual void operator++() {}
Diagnostic(ErrorCode.ERR_NewVirtualInSealed, op).WithArguments("C1.operator " + op + @"()", "C1").WithLocation(4, 33),
// (9,42): error CS0549: 'C2.operator checked ++()' is a new virtual member in sealed type 'C2'
// public virtual void operator checked ++() {}
Diagnostic(ErrorCode.ERR_NewVirtualInSealed, op).WithArguments("C2.operator checked " + op + @"()", "C2").WithLocation(9, 42)
);
}
[Theory]
[CombinatorialData]
public void Increment_028_VirtualNotAllowedInClass_OnStatic([CombinatorialValues("++", "--")] string op)
{
var source = @"
class C1
{
public static virtual C1 operator" + op + @"(C1 x) => throw null;
}
class C2
{
public static virtual C2 operator checked " + op + @"(C2 x) => throw null;
public static C2 operator " + op + @"(C2 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,38): error CS0106: The modifier 'virtual' is not valid for this item
// public static virtual C1 operator++(C1 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(4, 38),
// (9,47): error CS0106: The modifier 'virtual' is not valid for this item
// public static virtual C2 operator checked ++(C2 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(9, 47)
);
}
[Theory]
[CombinatorialData]
public void Increment_029_VirtualNotAllowedInStruct([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct C1
{
public virtual void operator" + op + @"() {}
}
struct C2
{
public virtual void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
struct C3
{
public static virtual C3 operator" + op + @"(C3 x) => throw null;
}
struct C4
{
public static virtual C4 operator checked " + op + @"(C4 x) => throw null;
public static C4 operator " + op + @"(C4 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,33): error CS0106: The modifier 'virtual' is not valid for this item
// public virtual void operator++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(4, 33),
// (9,42): error CS0106: The modifier 'virtual' is not valid for this item
// public virtual void operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(9, 42),
// (15,38): error CS0106: The modifier 'virtual' is not valid for this item
// public static virtual C3 operator++(C3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(15, 38),
// (20,47): error CS0106: The modifier 'virtual' is not valid for this item
// public static virtual C4 operator checked ++(C4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(20, 47)
);
}
[Theory]
[CombinatorialData]
public void Increment_030_VirtualNotAllowedOnExplicitImplementation([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
" + typeKeyword + @" C3 : I1
{
virtual void I1.operator " + op + @"() {}
}
" + typeKeyword + @" C4 : I2
{
virtual void I2.operator checked " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,30): error CS0106: The modifier 'virtual' is not valid for this item
// virtual void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(15, 30),
// (20,38): error CS0106: The modifier 'virtual' is not valid for this item
// virtual void I2.operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(20, 38)
);
}
[Theory]
[CombinatorialData]
public void Increment_031_VirtualNotAllowedOnExplicitImplementation_OnStatic([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
static abstract I1 operator " + op + @"(I1 x);
}
interface I2
{
static abstract I2 operator checked " + op + @"(I2 x);
static sealed I2 operator " + op + @"(I2 x) => throw null;
}
" + typeKeyword + @" C3 : I1
{
static virtual I1 I1.operator " + op + @"(I1 x) => throw null;
}
" + typeKeyword + @" C4 : I2
{
static virtual I2 I2.operator checked " + op + @"(I2 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,35): error CS0106: The modifier 'virtual' is not valid for this item
// static virtual I1 I1.operator ++(I1 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(15, 35),
// (20,43): error CS0106: The modifier 'virtual' is not valid for this item
// static virtual I2 I2.operator checked ++(I2 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(20, 43)
);
}
[Theory]
[CombinatorialData]
public void Increment_032_VirtualAbstractNotAllowed([CombinatorialValues("++", "--")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public virtual abstract void operator" + op + @"() {}
public virtual abstract void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,42): error CS0503: The abstract method 'C1.operator ++()' cannot be marked virtual
// public virtual abstract void operator++() {}
Diagnostic(ErrorCode.ERR_AbstractNotVirtual, op).WithArguments("method", "C1.operator " + op + @"()").WithLocation(3, 42),
// (4,50): error CS0503: The abstract method 'C1.operator checked ++()' cannot be marked virtual
// public virtual abstract void operator checked++() {}
Diagnostic(ErrorCode.ERR_AbstractNotVirtual, op).WithArguments("method", "C1.operator checked " + op + @"()").WithLocation(4, 50)
);
}
[Theory]
[CombinatorialData]
public void Increment_033_OverrideAllowedInClass([CombinatorialValues("++", "--")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"() " + (abstractInBase ? ";" : "{}") + @"
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"() " + (abstractInBase ? ";" : "{}") + @"
}
class C2 : C1
{
public override void operator" + op + @"() {}
public override void operator checked" + op + @"() {}
}
class C3 : C2
{
public override void operator" + op + @"() {}
public override void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate1, sourceSymbolValidator: validate1).VerifyDiagnostics();
var source2 = @"
abstract class C1
{
public virtual void operator" + op + @"() {}
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"() " + (abstractInBase ? ";" : "{}") + @"
}
class C2 : C1
{
public override void operator checked" + op + @"() {}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp2, symbolValidator: validate2, sourceSymbolValidator: validate2).VerifyDiagnostics();
void validate1(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
void validate2(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m, MethodSymbol overridden)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Same(overridden, m.OverriddenMethod);
}
}
[Theory]
[CombinatorialData]
public void Increment_034_AbstractOverrideAllowedInClass([CombinatorialValues("++", "--")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"() " + (abstractInBase ? ";" : "{}") + @"
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"() " + (abstractInBase ? ";" : "{}") + @"
}
abstract class C2 : C1
{
public abstract override void operator" + op + @"();
public abstract override void operator checked" + op + @"();
}
class C3 : C2
{
public override void operator" + op + @"() {}
public override void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m, MethodSymbol overridden)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.Equal(m.ContainingType.Name == "C2", m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Same(overridden, m.OverriddenMethod);
}
comp = CreateCompilation(["class C4 : C2 {}", source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (1,7): error CS0534: 'C4' does not implement inherited abstract member 'C2.operator checked ++()'
// class C3 : C2 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C4").WithArguments("C4", "C2.operator checked " + op + @"()").WithLocation(1, 7),
// (1,7): error CS0534: 'C4' does not implement inherited abstract member 'C2.operator ++()'
// class C3 : C2 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C4").WithArguments("C4", "C2.operator " + op + @"()").WithLocation(1, 7)
);
}
[Theory]
[CombinatorialData]
public void Increment_035_OverrideAllowedInStruct([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct S1
{
public override void operator" + op + @"() {}
public override void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
validateOp(comp.GetMember<MethodSymbol>("S1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(comp.GetMember<MethodSymbol>("S1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
comp.VerifyDiagnostics(
// (4,34): error CS0115: 'S1.operator ++()': no suitable method found to override
// public override void operator++() {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("S1.operator " + op + @"()").WithLocation(4, 34),
// (5,42): error CS0115: 'S1.operator checked ++()': no suitable method found to override
// public override void operator checked++() {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("S1.operator checked " + op + @"()").WithLocation(5, 42)
);
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Null(m.OverriddenMethod);
}
}
[Theory]
[CombinatorialData]
public void Increment_036_OverrideNotAllowed_OnStatic([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public static override C1 operator" + op + @"(C1 x) => throw null;
}
" + typeKeyword + @" C2
{
public static override C2 operator checked " + op + @"(C2 x) => throw null;
public static C2 operator " + op + @"(C2 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,39): error CS0106: The modifier 'override' is not valid for this item
// public static override C1 operator++(C1 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(3, 39),
// (8,48): error CS0106: The modifier 'override' is not valid for this item
// public static override C2 operator checked ++(C2 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(8, 48)
);
}
[Theory]
[CombinatorialData]
public void Increment_037_OverrideNotAllowedInInterface([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
public override void operator" + op + @"() {}
}
interface I2
{
public override void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
interface I3
{
public static override I3 operator" + op + @"(I3 x) => throw null;
}
interface I4
{
public static override I4 operator checked " + op + @"(I4 x) => throw null;
public static I4 operator " + op + @"(I4 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (4,34): error CS0106: The modifier 'override' is not valid for this item
// public override void operator++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(4, 34),
// (9,43): error CS0106: The modifier 'override' is not valid for this item
// public override void operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(9, 43),
// (15,39): error CS0106: The modifier 'override' is not valid for this item
// public static override I3 operator++(I3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(15, 39),
// (20,48): error CS0106: The modifier 'override' is not valid for this item
// public static override I4 operator checked ++(I4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(20, 48)
);
}
[Theory]
[CombinatorialData]
public void Increment_038_OverrideNotAllowedOnExplicitImplementation([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
" + typeKeyword + @" C3 : I1
{
override void I1.operator " + op + @"() {}
}
" + typeKeyword + @" C4 : I2
{
override void I2.operator checked " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,31): error CS0106: The modifier 'override' is not valid for this item
// override void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(15, 31),
// (20,39): error CS0106: The modifier 'override' is not valid for this item
// override void I2.operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(20, 39)
);
}
[Theory]
[CombinatorialData]
public void Increment_039_OverrideNotAllowedOnExplicitImplementation_OnStatic([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
static abstract I1 operator " + op + @"(I1 x);
}
interface I2
{
static abstract I2 operator checked " + op + @"(I2 x);
static sealed I2 operator " + op + @"(I2 x) => throw null;
}
" + typeKeyword + @" C3 : I1
{
static override I1 I1.operator " + op + @"(I1 x) => throw null;
}
" + typeKeyword + @" C4 : I2
{
static override I2 I2.operator checked " + op + @"(I2 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,36): error CS0106: The modifier 'override' is not valid for this item
// static override I1 I1.operator ++(I1 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(15, 36),
// (20,44): error CS0106: The modifier 'override' is not valid for this item
// static override I2 I2.operator checked ++(I2 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(20, 44)
);
}
[Theory]
[CombinatorialData]
public void Increment_040_VirtualOverrideNotAllowed([CombinatorialValues("++", "--")] string op)
{
var source = @"
class C1
{
public virtual void operator" + op + @"() {}
public virtual void operator checked" + op + @"() {}
}
class C2 : C1
{
public virtual override void operator" + op + @"() {}
public virtual override void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (10,42): error CS0113: A member 'C2.operator ++()' marked as override cannot be marked as new or virtual
// public virtual override void operator++() {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C2.operator " + op + @"()").WithLocation(10, 42),
// (11,50): error CS0113: A member 'C2.operator checked ++()' marked as override cannot be marked as new or virtual
// public virtual override void operator checked++() {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C2.operator checked " + op + @"()").WithLocation(11, 50)
);
}
[Theory]
[CombinatorialData]
public void Increment_041_SealedAllowedInInterface([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface C1
{
sealed void operator" + op + @"() {}
sealed void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
var source2 = @"
class C3 : C1
{
void C1.operator" + op + @"() {}
void C1.operator checked" + op + @"() {}
}
";
comp = CreateCompilation([source2, source], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (4,21): error CS0539: 'C3.operator ++()' in explicit interface declaration is not found among members of the interface that can be implemented
// void C1.operator++() {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C3.operator " + op + @"()").WithLocation(4, 21),
// (5,29): error CS0539: 'C3.operator checked ++()' in explicit interface declaration is not found among members of the interface that can be implemented
// void C1.operator checked++() {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C3.operator checked " + op + @"()").WithLocation(5, 29)
);
}
[Theory]
[CombinatorialData]
public void Increment_042_SealedOverrideAllowedInClass([CombinatorialValues("++", "--")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"() " + (abstractInBase ? ";" : "{}") + @"
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"() " + (abstractInBase ? ";" : "{}") + @"
}
abstract class C2 : C1
{
public sealed override void operator" + op + @"() {}
public sealed override void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m, MethodSymbol overridden)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.True(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Same(overridden, m.OverriddenMethod);
}
var source2 = @"
class C3 : C2
{
public override void operator" + op + @"() {}
public override void operator checked" + op + @"() {}
}
";
comp = CreateCompilation([source2, source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,34): error CS0239: 'C3.operator ++()': cannot override inherited member 'C2.operator ++()' because it is sealed
// public override void operator++() {}
Diagnostic(ErrorCode.ERR_CantOverrideSealed, op).WithArguments("C3.operator " + op + @"()", "C2.operator " + op + @"()").WithLocation(4, 34),
// (5,42): error CS0239: 'C3.operator checked ++()': cannot override inherited member 'C2.operator checked ++()' because it is sealed
// public override void operator checked++() {}
Diagnostic(ErrorCode.ERR_CantOverrideSealed, op).WithArguments("C3.operator checked " + op + @"()", "C2.operator checked " + op + @"()").WithLocation(5, 42)
);
}
[Theory]
[CombinatorialData]
public void Increment_043_SealedNotAllowedInClass([CombinatorialValues("++", "--")] string op)
{
var source = @"
class C1
{
public sealed void operator" + op + @"() {}
}
class C2
{
public sealed void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
class C3
{
public static sealed C3 operator" + op + @"(C3 x) => throw null;
}
class C4
{
public static sealed C4 operator checked " + op + @"(C4 x) => throw null;
public static C4 operator " + op + @"(C4 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,32): error CS0238: 'C1.operator ++()' cannot be sealed because it is not an override
// public sealed void operator++() {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator " + op + @"()").WithLocation(4, 32),
// (9,41): error CS0238: 'C2.operator checked ++()' cannot be sealed because it is not an override
// public sealed void operator checked ++() {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C2.operator checked " + op + @"()").WithLocation(9, 41),
// (15,37): error CS0106: The modifier 'sealed' is not valid for this item
// public static sealed C3 operator++(C3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 37),
// (20,46): error CS0106: The modifier 'sealed' is not valid for this item
// public static sealed C4 operator checked ++(C4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 46)
);
}
[Theory]
[CombinatorialData]
public void Increment_044_SealedNotAllowedInStruct([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct C1
{
public sealed void operator" + op + @"() {}
}
struct C2
{
public sealed void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
struct C3
{
public static sealed C3 operator" + op + @"(C3 x) => throw null;
}
struct C4
{
public static sealed C4 operator checked " + op + @"(C4 x) => throw null;
public static C4 operator " + op + @"(C4 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,32): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed void operator++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 32),
// (9,41): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed void operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(9, 41),
// (15,37): error CS0106: The modifier 'sealed' is not valid for this item
// public static sealed C3 operator++(C3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 37),
// (20,46): error CS0106: The modifier 'sealed' is not valid for this item
// public static sealed C4 operator checked ++(C4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 46)
);
}
[Theory]
[CombinatorialData]
public void Increment_045_SealedOverrideNotAllowedInStruct([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct C1
{
public sealed override void operator" + op + @"() {}
}
struct C2
{
public sealed override void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
struct C3
{
public static sealed override C3 operator" + op + @"(C3 x) => throw null;
}
struct C4
{
public static sealed override C4 operator checked " + op + @"(C4 x) => throw null;
public static C4 operator " + op + @"(C4 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,41): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed override void operator++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 41),
// (4,41): error CS0115: 'C1.operator ++()': no suitable method found to override
// public sealed override void operator++() {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C1.operator " + op + @"()").WithLocation(4, 41),
// (9,50): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed override void operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(9, 50),
// (9,50): error CS0115: 'C2.operator checked ++()': no suitable method found to override
// public sealed override void operator checked ++() {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C2.operator checked " + op + @"()").WithLocation(9, 50),
// (15,46): error CS0106: The modifier 'sealed' is not valid for this item
// public static sealed override C3 operator++(C3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 46),
// (15,46): error CS0106: The modifier 'override' is not valid for this item
// public static sealed override C3 operator++(C3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(15, 46),
// (20,55): error CS0106: The modifier 'sealed' is not valid for this item
// public static sealed override C4 operator checked ++(C4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 55),
// (20,55): error CS0106: The modifier 'override' is not valid for this item
// public static sealed override C4 operator checked ++(C4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(20, 55)
);
}
[Theory]
[CombinatorialData]
public void Increment_046_SealedAbstractOverrideNotAllowedInStruct([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct C1
{
public sealed abstract override void operator" + op + @"() {}
}
struct C2
{
public sealed abstract override void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
struct C3
{
public static sealed abstract override C3 operator" + op + @"(C3 x) => throw null;
}
struct C4
{
public static sealed abstract override C4 operator checked " + op + @"(C4 x) => throw null;
public static C4 operator " + op + @"(C4 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,50): error CS0106: The modifier 'abstract' is not valid for this item
// public sealed abstract override void operator++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(4, 50),
// (4,50): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract override void operator++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 50),
// (4,50): error CS0115: 'C1.operator ++()': no suitable method found to override
// public sealed abstract override void operator++() {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C1.operator " + op + @"()").WithLocation(4, 50),
// (9,59): error CS0106: The modifier 'abstract' is not valid for this item
// public sealed abstract override void operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(9, 59),
// (9,59): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract override void operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(9, 59),
// (9,59): error CS0115: 'C2.operator checked ++()': no suitable method found to override
// public sealed abstract override void operator checked ++() {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C2.operator checked " + op + @"()").WithLocation(9, 59),
// (15,55): error CS0106: The modifier 'abstract' is not valid for this item
// public static sealed abstract override C3 operator++(C3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(15, 55),
// (15,55): error CS0106: The modifier 'sealed' is not valid for this item
// public static sealed abstract override C3 operator++(C3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 55),
// (15,55): error CS0106: The modifier 'override' is not valid for this item
// public static sealed abstract override C3 operator++(C3 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(15, 55),
// (20,64): error CS0106: The modifier 'abstract' is not valid for this item
// public static sealed abstract override C4 operator checked ++(C4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(20, 64),
// (20,64): error CS0106: The modifier 'sealed' is not valid for this item
// public static sealed abstract override C4 operator checked ++(C4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 64),
// (20,64): error CS0106: The modifier 'override' is not valid for this item
// public static sealed abstract override C4 operator checked ++(C4 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(20, 64)
);
}
[Theory]
[CombinatorialData]
public void Increment_047_SealedNotAllowedOnExplicitImplementation([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
" + typeKeyword + @" C3 : I1
{
sealed void I1.operator " + op + @"() {}
}
" + typeKeyword + @" C4 : I2
{
sealed void I2.operator checked " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,29): error CS0106: The modifier 'sealed' is not valid for this item
// sealed void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 29),
// (20,37): error CS0106: The modifier 'sealed' is not valid for this item
// sealed void I2.operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 37)
);
}
[Theory]
[CombinatorialData]
public void Increment_048_SealedNotAllowedOnExplicitImplementation_OnStatic([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
static abstract I1 operator " + op + @"(I1 x);
}
interface I2
{
static abstract I2 operator checked " + op + @"(I2 x);
static sealed I2 operator " + op + @"(I2 x) => throw null;
}
" + typeKeyword + @" C3 : I1
{
static sealed I1 I1.operator " + op + @"(I1 x) => throw null;
}
" + typeKeyword + @" C4 : I2
{
static sealed I2 I2.operator checked " + op + @"(I2 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,34): error CS0106: The modifier 'sealed' is not valid for this item
// static sealed I1 I1.operator ++(I1 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 34),
// (20,42): error CS0106: The modifier 'sealed' is not valid for this item
// static sealed I2 I2.operator checked ++(I2 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 42)
);
}
[Theory]
[CombinatorialData]
public void Increment_049_SealedAbstractNotAllowed([CombinatorialValues("++", "--")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public sealed abstract void operator" + op + @"();
public sealed abstract void operator checked" + op + @"();
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
if (typeKeyword == "interface")
{
comp.VerifyDiagnostics(
// (3,41): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract void operator--();
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(3, 41),
// (4,49): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract void operator checked--();
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 49)
);
}
else
{
comp.VerifyDiagnostics(
// (3,41): error CS0238: 'C1.operator ++()' cannot be sealed because it is not an override
// public sealed abstract void operator++();
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator " + op + @"()").WithLocation(3, 41),
// (4,49): error CS0238: 'C1.operator checked ++()' cannot be sealed because it is not an override
// public sealed abstract void operator checked++();
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator checked " + op + @"()").WithLocation(4, 49)
);
}
}
[Theory]
[CombinatorialData]
public void Increment_050_SealedAbstractOverrideNotAllowedInClass([CombinatorialValues("++", "--")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"() " + (abstractInBase ? ";" : "{}") + @"
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"() " + (abstractInBase ? ";" : "{}") + @"
}
abstract class C2 : C1
{
public sealed abstract override void operator" + op + @"();
public sealed abstract override void operator checked" + op + @"();
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (10,50): error CS0502: 'C2.operator ++()' cannot be both abstract and sealed
// public sealed abstract override void operator++();
Diagnostic(ErrorCode.ERR_AbstractAndSealed, op).WithArguments("C2.operator " + op + @"()").WithLocation(10, 50),
// (11,58): error CS0502: 'C2.operator checked ++()' cannot be both abstract and sealed
// public sealed abstract override void operator checked++();
Diagnostic(ErrorCode.ERR_AbstractAndSealed, op).WithArguments("C2.operator checked " + op + @"()").WithLocation(11, 58)
);
}
[Theory]
[CombinatorialData]
public void Increment_051_SealedAbstractNotAllowedOnExplicitImplementation([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
interface I3 : I1
{
sealed abstract void I1.operator " + op + @"();
}
interface I4 : I2
{
sealed abstract void I2.operator checked " + op + @"();
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,38): error CS0106: The modifier 'sealed' is not valid for this item
// sealed abstract void I1.operator ++();
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 38),
// (20,46): error CS0106: The modifier 'sealed' is not valid for this item
// sealed abstract void I2.operator checked ++();
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 46)
);
}
[Theory]
[CombinatorialData]
public void Increment_052_SealedAbstractNotAllowedOnExplicitImplementation_OnStatic([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
static abstract I1 operator " + op + @"(I1 x);
}
interface I2
{
static abstract I2 operator checked " + op + @"(I2 x);
static sealed I2 operator " + op + @"(I2 x) => throw null;
}
interface I3 : I1
{
static sealed abstract I1 I1.operator " + op + @"(I1 x);
}
interface I4 : I2
{
static sealed abstract I2 I2.operator checked " + op + @"(I2 x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,43): error CS0106: The modifier 'sealed' is not valid for this item
// static sealed abstract I1 I1.operator ++(I1 x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 43),
// (20,51): error CS0106: The modifier 'sealed' is not valid for this item
// static sealed abstract I2 I2.operator checked ++(I2 x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 51)
);
}
[Theory]
[CombinatorialData]
public void Increment_053_SealedVirtualNotAllowed([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public virtual sealed void operator" + op + @"() {}
public virtual sealed void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
if (typeKeyword == "interface")
{
comp.VerifyDiagnostics(
// (3,40): error CS0106: The modifier 'sealed' is not valid for this item
// public virtual sealed void operator++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(3, 40),
// (4,48): error CS0106: The modifier 'sealed' is not valid for this item
// public virtual sealed void operator checked++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 48)
);
}
else
{
comp.VerifyDiagnostics(
// (3,40): error CS0238: 'C1.operator ++()' cannot be sealed because it is not an override
// public virtual sealed void operator++() {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator " + op + @"()").WithLocation(3, 40),
// (4,48): error CS0238: 'C1.operator checked ++()' cannot be sealed because it is not an override
// public virtual sealed void operator checked++() {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator checked " + op + @"()").WithLocation(4, 48)
);
}
}
[Theory]
[CombinatorialData]
public void Increment_054_NewAllowedInInterface_WRN_NewRequired([CombinatorialValues("++", "--")] string op, [CombinatorialValues("abstract", "virtual", "sealed")] string baseModifier)
{
var source = @"
interface C1
{
public " + baseModifier + @" void operator" + op + @"() " + (baseModifier == "abstract" ? ";" : "{}") + @"
public " + baseModifier + @" void operator checked" + op + @"() " + (baseModifier == "abstract" ? ";" : "{}") + @"
}
interface C2 : C1
{
public void operator" + op + @"() {}
public void operator checked" + op + @"() {}
}
interface C3 : C1
{
public new void operator" + op + @"() {}
public new void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (10,25): warning CS0108: 'C2.operator ++()' hides inherited member 'C1.operator ++()'. Use the new keyword if hiding was intended.
// public void operator++() {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator " + op + @"()", "C1.operator " + op + @"()").WithLocation(10, 25),
// (11,33): warning CS0108: 'C2.operator checked ++()' hides inherited member 'C1.operator checked ++()'. Use the new keyword if hiding was intended.
// public void operator checked++() {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator checked " + op + @"()", "C1.operator checked " + op + @"()").WithLocation(11, 33)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void Increment_055_NewAllowedInClass_WRN_NewRequired([CombinatorialValues("++", "--")] string op)
{
var source = @"
class C1
{
public void operator" + op + @"() {}
public void operator checked" + op + @"() {}
}
class C2 : C1
{
public void operator" + op + @"() {}
public void operator checked" + op + @"() {}
}
class C3 : C1
{
public new void operator" + op + @"() {}
public new void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(
// (10,25): warning CS0108: 'C2.operator ++()' hides inherited member 'C1.operator ++()'. Use the new keyword if hiding was intended.
// public void operator++() {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator " + op + @"()", "C1.operator " + op + @"()").WithLocation(10, 25),
// (11,33): warning CS0108: 'C2.operator checked ++()' hides inherited member 'C1.operator checked ++()'. Use the new keyword if hiding was intended.
// public void operator checked++() {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator checked " + op + @"()", "C1.operator checked " + op + @"()").WithLocation(11, 33)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void Increment_056_NewAllowedInClass_WRN_NewOrOverrideExpected([CombinatorialValues("++", "--")] string op)
{
var source = @"
class C1
{
public virtual void operator" + op + @"() {}
public virtual void operator checked" + op + @"() {}
}
class C2 : C1
{
public void operator" + op + @"() {}
public void operator checked" + op + @"() {}
}
class C3 : C1
{
public new void operator" + op + @"() {}
public new void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(
// (10,25): warning CS0114: 'C2.operator ++()' hides inherited member 'C1.operator ++()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator++() {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator " + op + @"()", "C1.operator " + op + @"()").WithLocation(10, 25),
// (11,33): warning CS0114: 'C2.operator checked ++()' hides inherited member 'C1.operator checked ++()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator checked++() {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator checked " + op + @"()", "C1.operator checked " + op + @"()").WithLocation(11, 33)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void Increment_057_NewAllowedInClass_ERR_HidingAbstractMethod([CombinatorialValues("++", "--")] string op)
{
var source = @"
abstract class C1
{
public abstract void operator" + op + @"();
public abstract void operator checked" + op + @"();
}
abstract class C2 : C1
{
public void operator" + op + @"() {}
public void operator checked" + op + @"() {}
}
abstract class C3 : C1
{
public new void operator" + op + @"() {}
public new void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (10,25): error CS0533: 'C2.operator ++()' hides inherited abstract member 'C1.operator ++()'
// public void operator++() {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C2.operator " + op + @"()", "C1.operator " + op + @"()").WithLocation(10, 25),
// (10,25): warning CS0114: 'C2.operator ++()' hides inherited member 'C1.operator ++()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator++() {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator " + op + @"()", "C1.operator " + op + @"()").WithLocation(10, 25),
// (11,33): error CS0533: 'C2.operator checked ++()' hides inherited abstract member 'C1.operator checked ++()'
// public void operator checked++() {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C2.operator checked " + op + @"()", "C1.operator checked " + op + @"()").WithLocation(11, 33),
// (11,33): warning CS0114: 'C2.operator checked ++()' hides inherited member 'C1.operator checked ++()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator checked++() {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator checked " + op + @"()", "C1.operator checked " + op + @"()").WithLocation(11, 33),
// (16,29): error CS0533: 'C3.operator ++()' hides inherited abstract member 'C1.operator ++()'
// public new void operator++() {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C3.operator " + op + @"()", "C1.operator " + op + @"()").WithLocation(16, 29),
// (17,37): error CS0533: 'C3.operator checked ++()' hides inherited abstract member 'C1.operator checked ++()'
// public new void operator checked++() {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C3.operator checked " + op + @"()", "C1.operator checked " + op + @"()").WithLocation(17, 37)
);
}
[Theory]
[CombinatorialData]
public void Increment_058_NewAllowed_WRN_NewNotRequired([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public new void operator" + op + @"() {}
public new void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (3,29): warning CS0109: The member 'C1.operator ++()' does not hide an accessible member. The new keyword is not required.
// public new void operator++() {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"()").WithLocation(3, 29),
// (4,37): warning CS0109: The member 'C1.operator checked ++()' does not hide an accessible member. The new keyword is not required.
// public new void operator checked++() {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator checked " + op + @"()").WithLocation(4, 37)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.Equal(typeKeyword == "interface", m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void Increment_059_NewAbstractAllowedInClassAndInterface([CombinatorialValues("++", "--")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public new abstract void operator" + op + @"();
public new abstract void operator checked" + op + @"();
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (3,38): warning CS0109: The member 'C1.operator ++()' does not hide an accessible member. The new keyword is not required.
// public new abstract void operator++();
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"()").WithLocation(3, 38),
// (4,46): warning CS0109: The member 'C1.operator checked ++()' does not hide an accessible member. The new keyword is not required.
// public new abstract void operator checked++();
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator checked " + op + @"()").WithLocation(4, 46)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void Increment_060_NewVirtualAllowedInClassAndInterface([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public new virtual void operator" + op + @"() {}
public new virtual void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (3,37): warning CS0109: The member 'C1.operator ++()' does not hide an accessible member. The new keyword is not required.
// public new virtual void operator++() {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"()").WithLocation(3, 37),
// (4,45): warning CS0109: The member 'C1.operator checked ++()' does not hide an accessible member. The new keyword is not required.
// public new virtual void operator checked++() {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator checked " + op + @"()").WithLocation(4, 45)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void Increment_061_NewSealedAllowedInInterface([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface C1
{
sealed new void operator" + op + @"() {}
sealed new void operator checked" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (4,29): warning CS0109: The member 'C1.operator ++()' does not hide an accessible member. The new keyword is not required.
// sealed new void operator++() {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"()").WithLocation(4, 29),
// (5,37): warning CS0109: The member 'C1.operator checked ++()' does not hide an accessible member. The new keyword is not required.
// sealed new void operator checked++() {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator checked " + op + @"()").WithLocation(5, 37)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void Increment_062_NewOverrideNotAllowedInClass([CombinatorialValues("++", "--")] string op)
{
var source = @"
class C1
{
public virtual void operator" + op + @"() {}
}
class C2
{
public virtual void operator checked " + op + @"() {}
public void operator " + op + @"() {}
}
class C3 : C1
{
public new override void operator" + op + @"() {}
}
class C4 : C2
{
public new override void operator checked " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (15,38): error CS0113: A member 'C3.operator ++()' marked as override cannot be marked as new or virtual
// public new override void operator++() {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C3.operator " + op + @"()").WithLocation(15, 38),
// (20,47): error CS0113: A member 'C4.operator checked ++()' marked as override cannot be marked as new or virtual
// public new override void operator checked ++() {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C4.operator checked " + op + @"()").WithLocation(20, 47)
);
}
[Theory]
[CombinatorialData]
public void Increment_063_NewOverrideNotAllowedInStruct([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct S1
{
public new override void operator" + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,38): error CS0113: A member 'S1.operator ++()' marked as override cannot be marked as new or virtual
// public new override void operator++() {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("S1.operator " + op + @"()").WithLocation(4, 38),
// (4,38): error CS0115: 'S1.operator ++()': no suitable method found to override
// public new override void operator++() {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("S1.operator " + op + @"()").WithLocation(4, 38)
);
}
[Theory]
[CombinatorialData]
public void Increment_064_NewNotAllowed_OnStatic([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public static new C1 operator" + op + @"(C1 x) => throw null;
public static new C1 operator checked " + op + @"(C1 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,34): error CS0106: The modifier 'new' is not valid for this item
// public static new C1 operator++(C1 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("new").WithLocation(3, 34),
// (4,43): error CS0106: The modifier 'new' is not valid for this item
// public static new C1 operator checked ++(C1 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("new").WithLocation(4, 43)
);
}
[Theory]
[CombinatorialData]
public void Increment_065_NewNotAllowedOnExplicitImplementation([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
" + typeKeyword + @" C3 : I1
{
new void I1.operator " + op + @"() {}
}
" + typeKeyword + @" C4 : I2
{
new void I2.operator checked " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,26): error CS0106: The modifier 'new' is not valid for this item
// new void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("new").WithLocation(15, 26),
// (20,34): error CS0106: The modifier 'new' is not valid for this item
// new void I2.operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("new").WithLocation(20, 34)
);
}
[Theory]
[CombinatorialData]
public void Increment_066_NewNotAllowedOnExplicitImplementation_OnStatic([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
static abstract I1 operator " + op + @"(I1 x);
}
interface I2
{
static abstract I2 operator checked " + op + @"(I2 x);
static sealed I2 operator " + op + @"(I2 x) => throw null;
}
" + typeKeyword + @" C3 : I1
{
static new I1 I1.operator " + op + @"(I1 x) => throw null;
}
" + typeKeyword + @" C4 : I2
{
static new I2 I2.operator checked " + op + @"(I2 x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,31): error CS0106: The modifier 'new' is not valid for this item
// static new I1 I1.operator ++(I1 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("new").WithLocation(15, 31),
// (20,39): error CS0106: The modifier 'new' is not valid for this item
// static new I2 I2.operator checked ++(I2 x) => throw null;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("new").WithLocation(20, 39)
);
}
[Theory]
[CombinatorialData]
public void Increment_067_ExplicitImplementationStaticVsInstanceMismatch([CombinatorialValues("++", "--")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
" + typeKeyword + @" C3
: I1
{
static void I1.operator " + op + @"() {}
}
" + typeKeyword + @" C4
: I2
{
static void I2.operator checked " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (16,29): error CS0106: The modifier 'static' is not valid for this item
// static void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(16, 29),
// (22,37): error CS0106: The modifier 'static' is not valid for this item
// static void I2.operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(22, 37)
);
}
[Theory]
[CombinatorialData]
public void Increment_068_ExplicitImplementationStaticVsInstanceMismatch([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"();
}
interface I2
{
void operator checked " + op + @"();
sealed void operator " + op + @"() {}
}
interface I3 : I1
{
static void I1.operator " + op + @"() {}
}
interface I4 : I2
{
static void I2.operator checked " + op + @"() {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,29): error CS0106: The modifier 'static' is not valid for this item
// static void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(15, 29),
// (20,37): error CS0106: The modifier 'static' is not valid for this item
// static void I2.operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(20, 37)
);
}
[Theory]
[CombinatorialData]
public void Increment_069_Consumption_OnNonVariable([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public class C1
{
public int _F;
public void operator" + op + @"() => throw null;
public void operator checked" + op + @"() => throw null;
public static C1 operator" + op + @"(C1 x)
{
System.Console.Write(""[operator]"");
return new C1() { _F = x._F + 1 };
}
public static C1 operator checked" + op + @"(C1 x)
{
System.Console.Write(""[operator checked]"");
checked
{
return new C1() { _F = x._F + 1 };
}
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static C1 P {get; set;} = new C1();
static void Main()
{
C1 x;
" + op + @"P;
System.Console.WriteLine(P._F);
P" + op + @";
System.Console.WriteLine(P._F);
x = " + op + @"P;
System.Console.WriteLine(P._F);
x = P" + op + @";
System.Console.WriteLine(P._F);
checked
{
" + op + @"P;
System.Console.WriteLine(P._F);
P" + op + @";
System.Console.WriteLine(P._F);
x = " + op + @"P;
System.Console.WriteLine(P._F);
x = P" + op + @";
System.Console.WriteLine(P._F);
}
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp2, expectedOutput: @"
[operator]1
[operator]2
[operator]3
[operator]4
[operator checked]5
[operator checked]6
[operator checked]7
[operator checked]8
").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_070_Consumption_Prefix_NotUsed_Class([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"();
public void operator checked" + op + @"();
}
public class C1 : I1
{
public int _F;
public void operator" + op + @"()
{
System.Console.Write(""[operator]"");
_F++;
}
public void operator checked" + op + @"()
{
System.Console.Write(""[operator checked]"");
_F++;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
" + op + @"GetA(x)[Get0()];
}
static void Test2(C1[] x)
{
checked
{
" + op + @"GetA(x)[Get0()];
}
}
static void Test3<T>(T[] x) where T : class, I1
{
" + op + @"GetA(x)[Get0()];
}
static void Test4<T>(T[] x) where T : class, I1
{
checked
{
" + op + @"GetA(x)[Get0()];
}
}
static void Test5<T>(T[] x) where T : I1
{
" + op + @"GetA(x)[Get0()];
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
" + op + @"GetA(x)[Get0()];
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
const string expectedOutput = @"
[GetA][Get0][operator]1
[GetA][Get0][operator checked]2
[GetA][Get0][operator]3
[GetA][Get0][operator checked]4
[GetA][Get0][operator]5
[GetA][Get0][operator checked]6
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 20 (0x14)
.maxstack 2
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call ""C1[] Program.GetA<C1>(C1[])""
IL_0007: call ""int Program.Get0()""
IL_000c: ldelem.ref
IL_000d: callvirt ""void C1." + methodName + @"()""
IL_0012: nop
IL_0013: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 29 (0x1d)
.maxstack 2
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call ""T[] Program.GetA<T>(T[])""
IL_0007: call ""int Program.Get0()""
IL_000c: ldelem ""T""
IL_0011: box ""T""
IL_0016: callvirt ""void I1." + methodName + @"()""
IL_001b: nop
IL_001c: ret
}
");
verifier.VerifyIL("Program.Test5<T>(T[])",
@"
{
// Code size 32 (0x20)
.maxstack 2
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call ""T[] Program.GetA<T>(T[])""
IL_0007: call ""int Program.Get0()""
IL_000c: readonly.
IL_000e: ldelema ""T""
IL_0013: constrained. ""T""
IL_0019: callvirt ""void I1." + methodName + @"()""
IL_001e: nop
IL_001f: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.PrefixUnaryExpressionSyntax>().First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "()", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
IIncrementOrDecrementOperation (Prefix) (OperatorMethod: void C1." + methodName + @"()) (OperationKind." + (op == "++" ? "Increment" : "Decrement") + @", Type: System.Void) (Syntax: '" + op + @"GetA(x)[Get0()]')
Target:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
");
methodName = (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 22 (0x16)
.maxstack 2
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: call ""C1[] Program.GetA<C1>(C1[])""
IL_0008: call ""int Program.Get0()""
IL_000d: ldelem.ref
IL_000e: callvirt ""void C1." + methodName + @"()""
IL_0013: nop
IL_0014: nop
IL_0015: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 31 (0x1f)
.maxstack 2
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: call ""T[] Program.GetA<T>(T[])""
IL_0008: call ""int Program.Get0()""
IL_000d: ldelem ""T""
IL_0012: box ""T""
IL_0017: callvirt ""void I1." + methodName + @"()""
IL_001c: nop
IL_001d: nop
IL_001e: ret
}
");
verifier.VerifyIL("Program.Test6<T>(T[])",
@"
{
// Code size 34 (0x22)
.maxstack 2
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: call ""T[] Program.GetA<T>(T[])""
IL_0008: call ""int Program.Get0()""
IL_000d: readonly.
IL_000f: ldelema ""T""
IL_0014: constrained. ""T""
IL_001a: callvirt ""void I1." + methodName + @"()""
IL_001f: nop
IL_0020: nop
IL_0021: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.PrefixUnaryExpressionSyntax>().Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "()", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
IIncrementOrDecrementOperation (Prefix, Checked) (OperatorMethod: void I1." + methodName + @"()) (OperationKind." + (op == "++" ? "Increment" : "Decrement") + @", Type: System.Void) (Syntax: '" + op + @"GetA(x)[Get0()]')
Target:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,9): error CS0023: Operator '++' cannot be applied to operand of type 'C1'
// ++GetA(x)[Get0()];
Diagnostic(ErrorCode.ERR_BadUnaryOp, op +"GetA(x)[Get0()]").WithArguments(op, "C1").WithLocation(23, 9),
// (30,13): error CS0023: Operator '++' cannot be applied to operand of type 'C1'
// ++GetA(x)[Get0()];
Diagnostic(ErrorCode.ERR_BadUnaryOp, op +"GetA(x)[Get0()]").WithArguments(op, "C1").WithLocation(30, 13),
// (36,9): error CS0023: Operator '++' cannot be applied to operand of type 'T'
// ++GetA(x)[Get0()];
Diagnostic(ErrorCode.ERR_BadUnaryOp, op +"GetA(x)[Get0()]").WithArguments(op, "T").WithLocation(36, 9),
// (43,13): error CS0023: Operator '++' cannot be applied to operand of type 'T'
// ++GetA(x)[Get0()];
Diagnostic(ErrorCode.ERR_BadUnaryOp, op +"GetA(x)[Get0()]").WithArguments(op, "T").WithLocation(43, 13),
// (49,9): error CS0023: Operator '++' cannot be applied to operand of type 'T'
// ++GetA(x)[Get0()];
Diagnostic(ErrorCode.ERR_BadUnaryOp, op +"GetA(x)[Get0()]").WithArguments(op, "T").WithLocation(49, 9),
// (56,13): error CS0023: Operator '++' cannot be applied to operand of type 'T'
// ++GetA(x)[Get0()];
Diagnostic(ErrorCode.ERR_BadUnaryOp, op +"GetA(x)[Get0()]").WithArguments(op, "T").WithLocation(56, 13)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
[Theory]
[CombinatorialData]
public void Increment_071_Consumption_Prefix_NotUsed_Struct([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"();
public void operator checked" + op + @"();
}
public struct C1 : I1
{
public int _F;
public void operator" + op + @"()
{
System.Console.Write(""[operator]"");
_F++;
}
public void operator checked" + op + @"()
{
System.Console.Write(""[operator checked]"");
_F++;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
" + op + @"GetA(x)[Get0()];
}
static void Test2(C1[] x)
{
checked
{
" + op + @"GetA(x)[Get0()];
}
}
static void Test3<T>(T[] x) where T : struct, I1
{
" + op + @"GetA(x)[Get0()];
}
static void Test4<T>(T[] x) where T : struct, I1
{
checked
{
" + op + @"GetA(x)[Get0()];
}
}
static void Test5<T>(T[] x) where T : I1
{
" + op + @"GetA(x)[Get0()];
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
" + op + @"GetA(x)[Get0()];
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp2, expectedOutput: @"
[GetA][Get0][operator]1
[GetA][Get0][operator checked]2
[GetA][Get0][operator]3
[GetA][Get0][operator checked]4
[GetA][Get0][operator]5
[GetA][Get0][operator checked]6
").VerifyDiagnostics();
var methodName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 24 (0x18)
.maxstack 2
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call ""C1[] Program.GetA<C1>(C1[])""
IL_0007: call ""int Program.Get0()""
IL_000c: ldelema ""C1""
IL_0011: call ""void C1." + methodName + @"()""
IL_0016: nop
IL_0017: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 32 (0x20)
.maxstack 2
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call ""T[] Program.GetA<T>(T[])""
IL_0007: call ""int Program.Get0()""
IL_000c: readonly.
IL_000e: ldelema ""T""
IL_0013: constrained. ""T""
IL_0019: callvirt ""void I1." + methodName + @"()""
IL_001e: nop
IL_001f: ret
}
");
methodName = (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 26 (0x1a)
.maxstack 2
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: call ""C1[] Program.GetA<C1>(C1[])""
IL_0008: call ""int Program.Get0()""
IL_000d: ldelema ""C1""
IL_0012: call ""void C1." + methodName + @"()""
IL_0017: nop
IL_0018: nop
IL_0019: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 34 (0x22)
.maxstack 2
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: call ""T[] Program.GetA<T>(T[])""
IL_0008: call ""int Program.Get0()""
IL_000d: readonly.
IL_000f: ldelema ""T""
IL_0014: constrained. ""T""
IL_001a: callvirt ""void I1." + methodName + @"()""
IL_001f: nop
IL_0020: nop
IL_0021: ret
}
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
verifier = CompileAndVerify(comp2, expectedOutput: @"
[GetA][Get0][operator]1
[GetA][Get0][operator checked]2
[GetA][Get0][operator]3
[GetA][Get0][operator checked]4
[GetA][Get0][operator]5
[GetA][Get0][operator checked]6
").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_072_Consumption_Prefix_Used_Class([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"();
public void operator checked" + op + @"();
}
public class C1 : I1
{
public int _F;
public void operator" + op + @"()
{
System.Console.Write(""[operator]"");
_F++;
}
public void operator checked" + op + @"()
{
System.Console.Write(""[operator checked]"");
_F++;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
C1 y = Test1(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)x[0] == y);
y = Test2(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)x[0] == y);
y = Test3(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)x[0] == y);
y = Test4(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)x[0] == y);
y = Test5(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)x[0] == y);
y = Test6(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)x[0] == y);
}
static C1 Test1(C1[] x)
{
return " + op + @"GetA(x)[Get0()];
}
static C1 Test2(C1[] x)
{
checked
{
return " + op + @"GetA(x)[Get0()];
}
}
static T Test3<T>(T[] x) where T : class, I1
{
return " + op + @"GetA(x)[Get0()];
}
static T Test4<T>(T[] x) where T : class, I1
{
checked
{
return " + op + @"GetA(x)[Get0()];
}
}
static T Test5<T>(T[] x) where T : I1
{
return " + op + @"GetA(x)[Get0()];
}
static T Test6<T>(T[] x) where T : I1
{
checked
{
return " + op + @"GetA(x)[Get0()];
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp2, expectedOutput: @"
[GetA][Get0][operator]1True
[GetA][Get0][operator checked]2True
[GetA][Get0][operator]3True
[GetA][Get0][operator checked]4True
[GetA][Get0][operator]5True
[GetA][Get0][operator checked]6True
").VerifyDiagnostics();
var methodName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 19 (0x13)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: dup
IL_000d: callvirt ""void C1." + methodName + @"()""
IL_0012: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 28 (0x1c)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: dup
IL_0011: box ""T""
IL_0016: callvirt ""void I1." + methodName + @"()""
IL_001b: ret
}
");
verifier.VerifyIL("Program.Test5<T>(T[])",
@"
{
// Code size 75 (0x4b)
.maxstack 3
.locals init (T[] V_0,
int V_1,
T V_2,
T V_3)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: stloc.0
IL_0007: call ""int Program.Get0()""
IL_000c: stloc.1
IL_000d: ldloc.0
IL_000e: ldloc.1
IL_000f: ldelem ""T""
IL_0014: stloc.2
IL_0015: ldloca.s V_3
IL_0017: initobj ""T""
IL_001d: ldloc.3
IL_001e: box ""T""
IL_0023: brtrue.s IL_0034
IL_0025: ldloca.s V_2
IL_0027: constrained. ""T""
IL_002d: callvirt ""void I1." + methodName + @"()""
IL_0032: br.s IL_0049
IL_0034: ldloca.s V_2
IL_0036: constrained. ""T""
IL_003c: callvirt ""void I1." + methodName + @"()""
IL_0041: ldloc.0
IL_0042: ldloc.1
IL_0043: ldloc.2
IL_0044: stelem ""T""
IL_0049: ldloc.2
IL_004a: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.PrefixUnaryExpressionSyntax>().First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "()", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("C1", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
IIncrementOrDecrementOperation (Prefix) (OperatorMethod: void C1." + methodName + @"()) (OperationKind." + (op == "++" ? "Increment" : "Decrement") + @", Type: C1) (Syntax: '" + op + @"GetA(x)[Get0()]')
Target:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
");
methodName = (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 19 (0x13)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: dup
IL_000d: callvirt ""void C1." + methodName + @"()""
IL_0012: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 28 (0x1c)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: dup
IL_0011: box ""T""
IL_0016: callvirt ""void I1." + methodName + @"()""
IL_001b: ret
}
");
verifier.VerifyIL("Program.Test6<T>(T[])",
@"
{
// Code size 75 (0x4b)
.maxstack 3
.locals init (T[] V_0,
int V_1,
T V_2,
T V_3)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: stloc.0
IL_0007: call ""int Program.Get0()""
IL_000c: stloc.1
IL_000d: ldloc.0
IL_000e: ldloc.1
IL_000f: ldelem ""T""
IL_0014: stloc.2
IL_0015: ldloca.s V_3
IL_0017: initobj ""T""
IL_001d: ldloc.3
IL_001e: box ""T""
IL_0023: brtrue.s IL_0034
IL_0025: ldloca.s V_2
IL_0027: constrained. ""T""
IL_002d: callvirt ""void I1." + methodName + @"()""
IL_0032: br.s IL_0049
IL_0034: ldloca.s V_2
IL_0036: constrained. ""T""
IL_003c: callvirt ""void I1." + methodName + @"()""
IL_0041: ldloc.0
IL_0042: ldloc.1
IL_0043: ldloc.2
IL_0044: stelem ""T""
IL_0049: ldloc.2
IL_004a: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.PrefixUnaryExpressionSyntax>().Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "()", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("T", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
IIncrementOrDecrementOperation (Prefix, Checked) (OperatorMethod: void I1." + methodName + @"()) (OperationKind." + (op == "++" ? "Increment" : "Decrement") + @", Type: T) (Syntax: '" + op + @"GetA(x)[Get0()]')
Target:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: @"
[GetA][Get0][operator]1True
[GetA][Get0][operator checked]2True
[GetA][Get0][operator]3True
[GetA][Get0][operator checked]4True
[GetA][Get0][operator]5True
[GetA][Get0][operator checked]6True
").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_073_Consumption_Prefix_Used_Struct([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"();
public void operator checked" + op + @"();
}
public struct C1 : I1
{
public int _F;
public void operator" + op + @"()
{
System.Console.Write(""[operator]"");
_F++;
}
public void operator checked" + op + @"()
{
System.Console.Write(""[operator checked]"");
_F++;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
C1 y = Test1(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test2(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test3(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test4(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test5(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test6(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
}
static C1 Test1(C1[] x)
{
return " + op + @"GetA(x)[Get0()];
}
static C1 Test2(C1[] x)
{
checked
{
return " + op + @"GetA(x)[Get0()];
}
}
static T Test3<T>(T[] x) where T : struct, I1
{
return " + op + @"GetA(x)[Get0()];
}
static T Test4<T>(T[] x) where T : struct, I1
{
checked
{
return " + op + @"GetA(x)[Get0()];
}
}
static T Test5<T>(T[] x) where T : I1
{
return " + op + @"GetA(x)[Get0()];
}
static T Test6<T>(T[] x) where T : I1
{
checked
{
return " + op + @"GetA(x)[Get0()];
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp2, expectedOutput: @"
[GetA][Get0][operator]11
[GetA][Get0][operator checked]22
[GetA][Get0][operator]33
[GetA][Get0][operator checked]44
[GetA][Get0][operator]55
[GetA][Get0][operator checked]66
").VerifyDiagnostics();
var methodName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 38 (0x26)
.maxstack 2
.locals init (C1 V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: dup
IL_0011: ldobj ""C1""
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""void C1." + methodName + @"()""
IL_001e: ldloc.0
IL_001f: stobj ""C1""
IL_0024: ldloc.0
IL_0025: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 42 (0x2a)
.maxstack 3
.locals init (int V_0,
T V_1)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: stloc.0
IL_000c: dup
IL_000d: ldloc.0
IL_000e: ldelem ""T""
IL_0013: stloc.1
IL_0014: ldloca.s V_1
IL_0016: constrained. ""T""
IL_001c: callvirt ""void I1." + methodName + @"()""
IL_0021: ldloc.0
IL_0022: ldloc.1
IL_0023: stelem ""T""
IL_0028: ldloc.1
IL_0029: ret
}
");
methodName = (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 38 (0x26)
.maxstack 2
.locals init (C1 V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: dup
IL_0011: ldobj ""C1""
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""void C1." + methodName + @"()""
IL_001e: ldloc.0
IL_001f: stobj ""C1""
IL_0024: ldloc.0
IL_0025: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 42 (0x2a)
.maxstack 3
.locals init (int V_0,
T V_1)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: stloc.0
IL_000c: dup
IL_000d: ldloc.0
IL_000e: ldelem ""T""
IL_0013: stloc.1
IL_0014: ldloca.s V_1
IL_0016: constrained. ""T""
IL_001c: callvirt ""void I1." + methodName + @"()""
IL_0021: ldloc.0
IL_0022: ldloc.1
IL_0023: stelem ""T""
IL_0028: ldloc.1
IL_0029: ret
}
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: @"
[GetA][Get0][operator]11
[GetA][Get0][operator checked]22
[GetA][Get0][operator]33
[GetA][Get0][operator checked]44
[GetA][Get0][operator]55
[GetA][Get0][operator checked]66
").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_074_Consumption_Postfix_NotUsed_Class([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"();
public void operator checked" + op + @"();
}
public class C1 : I1
{
public int _F;
public void operator" + op + @"()
{
System.Console.Write(""[operator]"");
_F++;
}
public void operator checked" + op + @"()
{
System.Console.Write(""[operator checked]"");
_F++;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
GetA(x)[Get0()]" + op + @";
}
static void Test2(C1[] x)
{
checked
{
GetA(x)[Get0()]" + op + @";
}
}
static void Test3<T>(T[] x) where T : class, I1
{
GetA(x)[Get0()]" + op + @";
}
static void Test4<T>(T[] x) where T : class, I1
{
checked
{
GetA(x)[Get0()]" + op + @";
}
}
static void Test5<T>(T[] x) where T : I1
{
GetA(x)[Get0()]" + op + @";
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
GetA(x)[Get0()]" + op + @";
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][operator]1
[GetA][Get0][operator checked]2
[GetA][Get0][operator]3
[GetA][Get0][operator checked]4
[GetA][Get0][operator]5
[GetA][Get0][operator checked]6
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 18 (0x12)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: callvirt ""void C1." + methodName + @"()""
IL_0011: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 27 (0x1b)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: box ""T""
IL_0015: callvirt ""void I1." + methodName + @"()""
IL_001a: ret
}
");
verifier.VerifyIL("Program.Test5<T>(T[])",
@"
{
// Code size 30 (0x1e)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: constrained. ""T""
IL_0018: callvirt ""void I1." + methodName + @"()""
IL_001d: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.PostfixUnaryExpressionSyntax>().First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "()", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
IIncrementOrDecrementOperation (Postfix) (OperatorMethod: void C1." + methodName + @"()) (OperationKind." + (op == "++" ? "Increment" : "Decrement") + @", Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @"')
Target:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
");
methodName = (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 18 (0x12)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: callvirt ""void C1." + methodName + @"()""
IL_0011: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 27 (0x1b)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: box ""T""
IL_0015: callvirt ""void I1." + methodName + @"()""
IL_001a: ret
}
");
verifier.VerifyIL("Program.Test6<T>(T[])",
@"
{
// Code size 30 (0x1e)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: constrained. ""T""
IL_0018: callvirt ""void I1." + methodName + @"()""
IL_001d: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.PostfixUnaryExpressionSyntax>().Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "()", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
IIncrementOrDecrementOperation (Postfix, Checked) (OperatorMethod: void I1." + methodName + @"()) (OperationKind." + (op == "++" ? "Increment" : "Decrement") + @", Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @"')
Target:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,9): error CS0023: Operator '++' cannot be applied to operand of type 'C1'
// GetA(x)[Get0()]++;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "GetA(x)[Get0()]" + op).WithArguments(op, "C1").WithLocation(23, 9),
// (30,13): error CS0023: Operator '++' cannot be applied to operand of type 'C1'
// GetA(x)[Get0()]++;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "GetA(x)[Get0()]" + op).WithArguments(op, "C1").WithLocation(30, 13),
// (36,9): error CS0023: Operator '++' cannot be applied to operand of type 'T'
// GetA(x)[Get0()]++;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "GetA(x)[Get0()]" + op).WithArguments(op, "T").WithLocation(36, 9),
// (43,13): error CS0023: Operator '++' cannot be applied to operand of type 'T'
// GetA(x)[Get0()]++;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "GetA(x)[Get0()]" + op).WithArguments(op, "T").WithLocation(43, 13),
// (49,9): error CS0023: Operator '++' cannot be applied to operand of type 'T'
// GetA(x)[Get0()]++;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "GetA(x)[Get0()]" + op).WithArguments(op, "T").WithLocation(49, 9),
// (56,13): error CS0023: Operator '++' cannot be applied to operand of type 'T'
// GetA(x)[Get0()]++;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "GetA(x)[Get0()]" + op).WithArguments(op, "T").WithLocation(56, 13)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
[Theory]
[CombinatorialData]
public void Increment_075_Consumption_Postfix_NotUsed_Struct([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"();
public void operator checked" + op + @"();
}
public struct C1 : I1
{
public int _F;
public void operator" + op + @"()
{
System.Console.Write(""[operator]"");
_F++;
}
public void operator checked" + op + @"()
{
System.Console.Write(""[operator checked]"");
_F++;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
GetA(x)[Get0()]" + op + @";
}
static void Test2(C1[] x)
{
checked
{
GetA(x)[Get0()]" + op + @";
}
}
static void Test3<T>(T[] x) where T : struct, I1
{
GetA(x)[Get0()]" + op + @";
}
static void Test4<T>(T[] x) where T : struct, I1
{
checked
{
GetA(x)[Get0()]" + op + @";
}
}
static void Test5<T>(T[] x) where T : I1
{
GetA(x)[Get0()]" + op + @";
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
GetA(x)[Get0()]" + op + @";
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp2, expectedOutput: @"
[GetA][Get0][operator]1
[GetA][Get0][operator checked]2
[GetA][Get0][operator]3
[GetA][Get0][operator checked]4
[GetA][Get0][operator]5
[GetA][Get0][operator checked]6
").VerifyDiagnostics();
var methodName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 22 (0x16)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: call ""void C1." + methodName + @"()""
IL_0015: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 30 (0x1e)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: constrained. ""T""
IL_0018: callvirt ""void I1." + methodName + @"()""
IL_001d: ret
}
");
methodName = (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 22 (0x16)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: call ""void C1." + methodName + @"()""
IL_0015: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 30 (0x1e)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: constrained. ""T""
IL_0018: callvirt ""void I1." + methodName + @"()""
IL_001d: ret
}
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: @"
[GetA][Get0][operator]1
[GetA][Get0][operator checked]2
[GetA][Get0][operator]3
[GetA][Get0][operator checked]4
[GetA][Get0][operator]5
[GetA][Get0][operator checked]6
").VerifyDiagnostics();
}
[Fact]
public void Increment_076_Consumption_Postfix_For()
{
var source = @"
public class C1
{
public int _F;
public void operator ++()
{
_F++;
}
}
public class Program
{
static void Main()
{
C1 x = new C1();
for (x++; x._F < 4; x++)
{
System.Console.Write(x._F);
}
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: @"123").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_077_Consumption_Postfix_Used([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public class C1
{
public void operator" + op + @"() {}
public void operator checked" + op + @"() {}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1 x = new C1();
C1 y = x" + op + @";
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
comp2.VerifyDiagnostics(
// (7,16): error CS0023: Operator '++' cannot be applied to operand of type 'C1'
// C1 y = x++;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "x" + op).WithArguments(op, "C1").WithLocation(7, 16)
);
}
[Theory]
[CombinatorialData]
public void Increment_078_Consumption_Postfix_Used([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public class C1
{
public int _F;
public void operator" + op + @"() => throw null;
public void operator checked" + op + @"() => throw null;
public static C1 operator" + op + @"(C1 x)
{
System.Console.Write(""[operator]"");
return new C1() { _F = x._F + 1 };
}
public static C1 operator checked" + op + @"(C1 x)
{
System.Console.Write(""[operator checked]"");
return new C1() { _F = x._F + 1 };
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1 x = new C1();
C1 y = x" + op + @";
System.Console.Write(y._F);
System.Console.Write(x._F);
checked
{
y = x" + op + @";
}
System.Console.Write(y._F);
System.Console.Write(x._F);
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
CompileAndVerify(comp2, expectedOutput: "[operator]01[operator checked]12").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_079_Consumption_RegularVersionInCheckedContext([CombinatorialValues("++", "--")] string op, bool fromMetadata)
{
var source1 = @"
public class C1
{
public int _F;
public void operator" + op + @"()
{
System.Console.Write(""[operator]"");
_F++;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test2(x);
System.Console.WriteLine(x[0]._F);
}
static void Test2(C1[] x)
{
checked
{
" + op + @"GetA(x)[Get0()];
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp2, expectedOutput: @"[GetA][Get0][operator]1").VerifyDiagnostics();
var methodName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 22 (0x16)
.maxstack 2
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: call ""C1[] Program.GetA<C1>(C1[])""
IL_0008: call ""int Program.Get0()""
IL_000d: ldelem.ref
IL_000e: callvirt ""void C1." + methodName + @"()""
IL_0013: nop
IL_0014: nop
IL_0015: ret
}
");
}
[Theory]
[CombinatorialData]
public void Increment_080_Consumption_CheckedVersionInRegularContext([CombinatorialValues("++", "--")] string op)
{
var source1 = @"
public class C1
{
public int _F;
public void operator checked " + op + @"()
{
System.Console.Write(""[operator]"");
_F++;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
" + op + @"GetA(x)[Get0()];
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (13,9): error CS0023: Operator '++' cannot be applied to operand of type 'C1'
// ++GetA(x)[Get0()];
Diagnostic(ErrorCode.ERR_BadUnaryOp, op + "GetA(x)[Get0()]").WithArguments(op, "C1").WithLocation(13, 9)
);
}
[Theory]
[CombinatorialData]
public void Increment_081_Consumption_Shadowing([CombinatorialValues("++", "--")] string op)
{
var source1 = @"
public class C1
{
public void operator" + op + @"() => throw null;
public void operator checked" + op + @"() => throw null;
}
public class C2 : C1
{
public new void operator" + op + @"()
{
System.Console.Write(""[operator]"");
}
}
public class Program
{
static void Main()
{
var x = new C2();
" + op + @"x;
checked
{
" + op + @"x;
}
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "[operator][operator]").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_082_Consumption_Shadowing([CombinatorialValues("++", "--")] string op)
{
var source1_1 = @"
public class C1
{
public void operator" + op + @"() => throw null;
public void operator checked" + op + @"() => throw null;
}
public class C2 : C1
{
public new void operator checked " + op + @"() => throw null;
}
";
var comp1_1 = CreateCompilation([source1_1, CompilerFeatureRequiredAttribute], assemblyName: "C");
var source2 = @"
public class Test
{
public static void Main()
{
var x = new C2();
" + op + @"x;
checked
{
" + op + @"x;
}
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1_1.ToMetadataReference()]);
comp2.VerifyDiagnostics();
var source1_2 = @"
public class C1
{
public void operator" + op + @"()
{
System.Console.Write(""[operator]"");
}
public void operator checked" + op + @"() => throw null;
}
public class C2 : C1
{
public new void operator checked " + op + @"()
{
System.Console.Write(""[checked operator]"");
}
public new void operator " + op + @"() => throw null;
}
";
var source3 = @"
public class Program
{
static void Main()
{
Test.Main();
}
}
";
var comp1_2 = CreateCompilation([source1_2, CompilerFeatureRequiredAttribute], assemblyName: "C");
var comp3 = CreateCompilation([source3, CompilerFeatureRequiredAttribute], references: [comp1_2.EmitToImageReference(), comp2.EmitToImageReference()], options: TestOptions.DebugExe);
CompileAndVerify(comp3, expectedOutput: "[operator][checked operator]").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_083_Consumption_Shadowing([CombinatorialValues("++", "--")] string op)
{
var source1 = @"
public abstract class C1
{
public abstract void operator" + op + @"();
public void operator checked" + op + @"() => throw null;
}
public class C2 : C1
{
public new void operator checked " + op + @"()
{
System.Console.Write(""[checked operator]"");
}
public override void operator " + op + @"()
{
System.Console.Write(""[operator]"");
}
}
public class Program
{
static void Main()
{
var x = new C2();
" + op + @"x;
checked
{
" + op + @"x;
}
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "[operator][checked operator]").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_084_Consumption_Overriding([CombinatorialValues("++", "--")] string op)
{
var source1 = @"
public abstract class C1
{
public abstract void operator" + op + @"();
public abstract void operator checked" + op + @"();
}
public abstract class C2 : C1
{
public override void operator checked " + op + @"()
{
System.Console.Write(""[checked operator]"");
}
}
public class C3 : C2
{
public override void operator " + op + @"()
{
System.Console.Write(""[operator]"");
}
}
public class Program
{
static void Main()
{
var x = new C3();
" + op + @"x;
checked
{
" + op + @"x;
}
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "[operator][checked operator]").VerifyDiagnostics();
}
[Fact]
public void Increment_085_Consumption_Ambiguity()
{
var source1 = @"
public interface I1
{
public void operator ++();
}
public interface I2<T> where T : I2<T>
{
public void operator ++();
public abstract static T operator ++(T x);
public abstract static T operator --(T x);
}
public class Program
{
static void Test5<T>(T x) where T : I1, I2<T>
{
++x;
--x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp1.VerifyDiagnostics(
// (18,9): error CS0121: The call is ambiguous between the following methods or properties: 'I1.operator ++()' and 'I2<T>.operator ++()'
// ++x;
Diagnostic(ErrorCode.ERR_AmbigCall, "++").WithArguments("I1.operator ++()", "I2<T>.operator ++()").WithLocation(18, 9)
);
var tree = comp1.SyntaxTrees.First();
var model = comp1.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.PrefixUnaryExpressionSyntax>().First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Null(symbolInfo.Symbol);
Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason);
Assert.Equal(2, symbolInfo.CandidateSymbols.Length);
Assert.Equal("void I1.op_IncrementAssignment()", symbolInfo.CandidateSymbols[0].ToTestDisplayString());
Assert.Equal("void I2<T>.op_IncrementAssignment()", symbolInfo.CandidateSymbols[1].ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.PrefixUnaryExpressionSyntax>().Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("T I2<T>.op_Decrement(T x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
}
[Fact]
public void Increment_086_Consumption_UseStaticOperatorsInOldVersion()
{
var source1 = @"
public class C1
{
public int _F;
public void operator ++()
{
System.Console.Write(""[instance operator]"");
_F++;
}
public void operator checked ++()
{
System.Console.Write(""[instance operator checked]"");
checked
{
_F++;
}
}
public static C1 operator ++(C1 x)
{
System.Console.Write(""[static operator]"");
return new C1() { _F = x._F + 1 };
}
public static C1 operator checked ++(C1 x)
{
System.Console.Write(""[static operator checked]"");
checked
{
return new C1() { _F = x._F + 1 };
}
}
}
";
var comp1Ref = CreateCompilation([source1, CompilerFeatureRequiredAttribute]).EmitToImageReference();
var source2 = @"
public class Program
{
static C1 P = new C1();
static void Main()
{
C1 x;
++P;
System.Console.WriteLine(P._F);
P++;
System.Console.WriteLine(P._F);
x = ++P;
System.Console.WriteLine(P._F);
x = P++;
System.Console.WriteLine(P._F);
checked
{
++P;
System.Console.WriteLine(P._F);
P++;
System.Console.WriteLine(P._F);
x = ++P;
System.Console.WriteLine(P._F);
x = P++;
System.Console.WriteLine(P._F);
}
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1Ref], options: TestOptions.DebugExe);
CompileAndVerify(comp2, expectedOutput: @"
[instance operator]1
[instance operator]2
[instance operator]3
[static operator]4
[instance operator checked]5
[instance operator checked]6
[instance operator checked]7
[static operator checked]8
").VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1Ref], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
CompileAndVerify(comp2, expectedOutput: @"
[static operator]1
[static operator]2
[static operator]3
[static operator]4
[static operator checked]5
[static operator checked]6
[static operator checked]7
[static operator checked]8
").VerifyDiagnostics();
}
[Fact]
public void Increment_087_Consumption_Obsolete()
{
var source1 = @"
public class C1
{
[System.Obsolete(""Test"")]
public void operator ++() {}
}
public class Program
{
static void Main()
{
var x = new C1();
++x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1).VerifyDiagnostics(
// (13,9): warning CS0618: 'C1.operator ++()' is obsolete: 'Test'
// ++x;
Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "++x").WithArguments("C1.operator ++()", "Test").WithLocation(13, 9)
);
}
[Fact]
public void Increment_088_Consumption_UnmanagedCallersOnly()
{
var source1 = @"
public class C1
{
[System.Runtime.InteropServices.UnmanagedCallersOnly]
public void operator ++() {}
}
public class Program
{
static void Main()
{
var x = new C1();
++x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe);
comp1.VerifyDiagnostics(
// (4,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract, non-virtual methods or static local functions.
// [System.Runtime.InteropServices.UnmanagedCallersOnly]
Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "System.Runtime.InteropServices.UnmanagedCallersOnly").WithLocation(4, 6),
// (13,9): error CS8901: 'C1.operator ++()' is attributed with 'UnmanagedCallersOnly' and cannot be called directly. Obtain a function pointer to this method.
// ++x;
Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeCalledDirectly, "++x").WithArguments("C1.operator ++()").WithLocation(13, 9)
);
}
[Fact]
public void Increment_089_Consumption_NullableAnalysis()
{
var source1 = @"
public class C1
{
public void operator ++() {}
}
#nullable enable
public class Program
{
static void Main()
{
C1? x = null;
try
{
++x;
System.Console.Write(""unreachable"");
x.ToString();
}
catch (System.NullReferenceException)
{
System.Console.Write(""in catch"");
}
C1? y = new C1();
++y;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "in catch").VerifyDiagnostics(
// (17,15): warning CS8602: Dereference of a possibly null reference.
// ++x;
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(17, 15)
);
var source2 = @"
public class C1
{
public void operator ++() {}
}
#nullable enable
public class Program
{
static void Main()
{
C1? x = null;
if (false)
{
++x;
System.Console.Write(""unreachable"");
x.ToString();
}
System.Console.Write(""Done"");
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp2, expectedOutput: "Done").VerifyDiagnostics(
// (17,13): warning CS0162: Unreachable code detected
// ++x;
Diagnostic(ErrorCode.WRN_UnreachableCode, "++").WithLocation(17, 13)
);
}
[Fact]
public void Increment_090_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
public void operator ++(int x = 0) {}
public int operator --() { return 0; }
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
++x;
--x;
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (4,26): error CS0558: User-defined operator 'C1.operator ++(int)' must be declared static and public
// public void operator ++(int x = 0) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBeStaticAndPublic, "++").WithArguments("C1.operator ++(int)").WithLocation(4, 26),
// (4,26): error CS0559: The parameter type for ++ or -- operator must be the containing type
// public void operator ++(int x = 0) {}
Diagnostic(ErrorCode.ERR_BadIncDecSignature, "++").WithLocation(4, 26),
// (4,33): warning CS1066: The default value specified for parameter 'x' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
// public void operator ++(int x = 0) {}
Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "x").WithArguments("x").WithLocation(4, 33),
// (5,25): error CS9310: The return type for this operator must be void
// public int operator --() { return 0; }
Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, "--").WithLocation(5, 25)
);
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0023: Operator '++' cannot be applied to operand of type 'C1'
// ++x;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "++x").WithArguments("++", "C1").WithLocation(7, 9),
// (8,9): error CS0023: Operator '--' cannot be applied to operand of type 'C1'
// --x;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "--x").WithArguments("--", "C1").WithLocation(8, 9)
);
}
[Fact]
public void Increment_091_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
public void operator ++(params int[] x) {}
public void operator --(__arglist) {}
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
++x;
--x;
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (4,26): error CS0558: User-defined operator 'C1.operator ++(params int[])' must be declared static and public
// public void operator ++(params int[] x) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBeStaticAndPublic, "++").WithArguments("C1.operator ++(params int[])").WithLocation(4, 26),
// (4,26): error CS0559: The parameter type for ++ or -- operator must be the containing type
// public void operator ++(params int[] x) {}
Diagnostic(ErrorCode.ERR_BadIncDecSignature, "++").WithLocation(4, 26),
// (4,29): error CS1670: params is not valid in this context
// public void operator ++(params int[] x) {}
Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(4, 29),
// (5,26): error CS0558: User-defined operator 'C1.operator --()' must be declared static and public
// public void operator --(__arglist) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBeStaticAndPublic, "--").WithArguments("C1.operator --()").WithLocation(5, 26),
// (5,29): error CS1669: __arglist is not valid in this context
// public void operator --(__arglist) {}
Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(5, 29)
);
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0023: Operator '++' cannot be applied to operand of type 'C1'
// ++x;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "++x").WithArguments("++", "C1").WithLocation(7, 9),
// (8,9): error CS0023: Operator '--' cannot be applied to operand of type 'C1'
// --x;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "--x").WithArguments("--", "C1").WithLocation(8, 9)
);
var decrement = comp2.GetMember<MethodSymbol>("C1.op_Decrement");
Assert.False(decrement.IsVararg);
AssertEx.Equal("void C1.op_Decrement()", decrement.ToTestDisplayString());
}
[Fact]
public void Increment_092_ConflictWithRegular()
{
var source1 = @"
public class C1
{
public void operator ++() {}
public void op_IncrementAssignment() {}
}
public class C2
{
public static C2 operator ++(C2 x) => x;
public static C2 op_Increment(C2 x) => x;
}
public class C3
{
public void op_IncrementAssignment() {}
public void operator ++() {}
}
public class C4
{
public static C4 op_Increment(C4 x) => x;
public static C4 operator ++(C4 x) => x;
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (6,17): error CS0111: Type 'C1' already defines a member called 'op_IncrementAssignment' with the same parameter types
// public void op_IncrementAssignment() {}
Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "op_IncrementAssignment").WithArguments("op_IncrementAssignment", "C1").WithLocation(6, 17),
// (13,22): error CS0111: Type 'C2' already defines a member called 'op_Increment' with the same parameter types
// public static C2 op_Increment(C2 x) => x;
Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "op_Increment").WithArguments("op_Increment", "C2").WithLocation(13, 22),
// (20,26): error CS0111: Type 'C3' already defines a member called 'op_IncrementAssignment' with the same parameter types
// public void operator ++() {}
Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "++").WithArguments("op_IncrementAssignment", "C3").WithLocation(20, 26),
// (27,31): error CS0111: Type 'C4' already defines a member called 'op_Increment' with the same parameter types
// public static C4 operator ++(C4 x) => x;
Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "++").WithArguments("op_Increment", "C4").WithLocation(27, 31)
);
}
[Theory]
[CombinatorialData]
public void Increment_093_Consumption_RegularVsOperator(bool fromMetadata)
{
var source1 = @"
public class C1
{
public void op_IncrementAssignment() {}
}
public class C2
{
public void operator++() {}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1 x = new C1();
++x;
C2 y = new C2();
y.op_IncrementAssignment();
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0023: Operator '++' cannot be applied to operand of type 'C1'
// ++x;
Diagnostic(ErrorCode.ERR_BadUnaryOp, "++x").WithArguments("++", "C1").WithLocation(7, 9),
// (9,11): error CS0571: 'C2.operator ++()': cannot explicitly call operator or accessor
// y.op_IncrementAssignment();
Diagnostic(ErrorCode.ERR_CantCallSpecialMethod, "op_IncrementAssignment").WithArguments("C2.operator ++()").WithLocation(9, 11)
);
}
[Theory]
[CombinatorialData]
public void Increment_094_Override_RegularVsOperatorMismatch(bool fromMetadata)
{
var source1 = @"
abstract public class C1
{
public abstract void op_IncrementAssignment();
}
abstract public class C3
{
public abstract void operator ++();
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class C2 : C1
{
public override void operator ++() {}
}
public class C4 : C3
{
public override void op_IncrementAssignment() {}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()]);
comp2.VerifyDiagnostics(
// (4,35): error CS9312: 'C2.operator ++()': cannot override inherited member 'C1.op_IncrementAssignment()' because one of them is not an operator.
// public override void operator ++() {}
Diagnostic(ErrorCode.ERR_OperatorMismatchOnOverride, "++").WithArguments("C2.operator ++()", "C1.op_IncrementAssignment()").WithLocation(4, 35),
// (9,26): error CS9312: 'C4.op_IncrementAssignment()': cannot override inherited member 'C3.operator ++()' because one of them is not an operator.
// public override void op_IncrementAssignment() {}
Diagnostic(ErrorCode.ERR_OperatorMismatchOnOverride, "op_IncrementAssignment").WithArguments("C4.op_IncrementAssignment()", "C3.operator ++()").WithLocation(9, 26)
);
}
[Theory]
[CombinatorialData]
public void Increment_095_Implement_RegularVsOperatorMismatch(bool fromMetadata)
{
var source1 = @"
public interface I1
{
void op_IncrementAssignment();
}
public interface I2
{
void operator ++();
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class C1 : I1
{
public void operator ++() {}
}
public class C2 : I2
{
public void op_IncrementAssignment() {}
}
public class C3 : I1
{
void I1.operator ++() {}
}
public class C4 : I2
{
void I2.op_IncrementAssignment() {}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()]);
comp2.VerifyDiagnostics(
// (2,19): error CS9311: 'C1' does not implement interface member 'I1.op_IncrementAssignment()'. 'C1.operator ++()' cannot implement 'I1.op_IncrementAssignment()' because one of them is not an operator.
// public class C1 : I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C1", "I1.op_IncrementAssignment()", "C1.operator ++()").WithLocation(2, 19),
// (7,19): error CS9311: 'C2' does not implement interface member 'I2.operator ++()'. 'C2.op_IncrementAssignment()' cannot implement 'I2.operator ++()' because one of them is not an operator.
// public class C2 : I2
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I2").WithArguments("C2", "I2.operator ++()", "C2.op_IncrementAssignment()").WithLocation(7, 19),
// (12,19): error CS0535: 'C3' does not implement interface member 'I1.op_IncrementAssignment()'
// public class C3 : I1
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C3", "I1.op_IncrementAssignment()").WithLocation(12, 19),
// (14,22): error CS0539: 'C3.operator ++()' in explicit interface declaration is not found among members of the interface that can be implemented
// void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "++").WithArguments("C3.operator ++()").WithLocation(14, 22),
// (17,19): error CS0535: 'C4' does not implement interface member 'I2.operator ++()'
// public class C4 : I2
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C4", "I2.operator ++()").WithLocation(17, 19),
// (19,13): error CS0539: 'C4.op_IncrementAssignment()' in explicit interface declaration is not found among members of the interface that can be implemented
// void I2.op_IncrementAssignment() {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "op_IncrementAssignment").WithArguments("C4.op_IncrementAssignment()").WithLocation(19, 13)
);
}
[Theory]
[CombinatorialData]
public void Increment_096_Implement_RegularVsOperatorMismatch(bool fromMetadata)
{
var source1 = @"
public interface I1
{
void op_IncrementAssignment();
}
public interface I2
{
void operator ++();
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class C11
{
public void operator ++() {}
}
public class C12 : C11, I1
{
}
public class C21
{
public void op_IncrementAssignment() {}
}
public class C22 : C21, I2
{
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()]);
comp2.VerifyDiagnostics(
// (7,25): error CS9311: 'C12' does not implement interface member 'I1.op_IncrementAssignment()'. 'C11.operator ++()' cannot implement 'I1.op_IncrementAssignment()' because one of them is not an operator.
// public class C12 : C11, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C12", "I1.op_IncrementAssignment()", "C11.operator ++()").WithLocation(7, 25),
// (16,25): error CS9311: 'C22' does not implement interface member 'I2.operator ++()'. 'C21.op_IncrementAssignment()' cannot implement 'I2.operator ++()' because one of them is not an operator.
// public class C22 : C21, I2
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I2").WithArguments("C22", "I2.operator ++()", "C21.op_IncrementAssignment()").WithLocation(16, 25)
);
}
[Fact]
public void Increment_097_Implement_RegularVsOperatorMismatch()
{
var source = @"
public interface I1
{
public void operator ++()
{
System.Console.Write(""[I1.operator]"");
}
}
public class C1 : I1
{
public virtual void op_IncrementAssignment()
{
System.Console.Write(""[C1.op_IncrementAssignment]"");
}
}
public class C2
{
public virtual void op_IncrementAssignment()
{
System.Console.Write(""[C2.op_IncrementAssignment]"");
}
}
public class C3 : C2, I1
{
}
public class Program
{
static void Main()
{
I1 x = new C1();
++x;
x = new C3();
++x;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (10,19): error CS9311: 'C1' does not implement interface member 'I1.operator ++()'. 'C1.op_IncrementAssignment()' cannot implement 'I1.operator ++()' because one of them is not an operator.
// public class C1 : I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C1", "I1.operator ++()", "C1.op_IncrementAssignment()").WithLocation(10, 19),
// (26,23): error CS9311: 'C3' does not implement interface member 'I1.operator ++()'. 'C2.op_IncrementAssignment()' cannot implement 'I1.operator ++()' because one of them is not an operator.
// public class C3 : C2, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C3", "I1.operator ++()", "C2.op_IncrementAssignment()").WithLocation(26, 23)
);
}
[Fact]
public void Increment_098_Implement_RegularVsOperatorMismatch()
{
var source = @"
public interface I1
{
public void op_IncrementAssignment()
{
System.Console.Write(""[I1.operator]"");
}
}
public class C1 : I1
{
public virtual void operator ++()
{
System.Console.Write(""[C1.operator]"");
}
}
public class C2
{
public virtual void operator ++()
{
System.Console.Write(""[C2.operator]"");
}
}
public class C3 : C2, I1
{
}
public class Program
{
static void Main()
{
I1 x = new C1();
x.op_IncrementAssignment();
x = new C3();
x.op_IncrementAssignment();
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (10,19): error CS9311: 'C1' does not implement interface member 'I1.op_IncrementAssignment()'. 'C1.operator ++()' cannot implement 'I1.op_IncrementAssignment()' because one of them is not an operator.
// public class C1 : I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C1", "I1.op_IncrementAssignment()", "C1.operator ++()").WithLocation(10, 19),
// (26,23): error CS9311: 'C3' does not implement interface member 'I1.op_IncrementAssignment()'. 'C2.operator ++()' cannot implement 'I1.op_IncrementAssignment()' because one of them is not an operator.
// public class C3 : C2, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C3", "I1.op_IncrementAssignment()", "C2.operator ++()").WithLocation(26, 23)
);
}
[Fact]
public void Increment_099_Implement_RegularVsOperatorMismatch()
{
var source = @"
public interface I1
{
public void operator ++();
}
public class C2 : I1
{
void I1.operator ++()
{
System.Console.Write(""[C2.operator]"");
}
}
public class C3 : C2, I1
{
public virtual void op_IncrementAssignment()
{
System.Console.Write(""[C3.op_IncrementAssignment]"");
}
}
public class Program
{
static void Main()
{
I1 x = new C3();
++x;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (15,23): error CS9311: 'C3' does not implement interface member 'I1.operator ++()'. 'C3.op_IncrementAssignment()' cannot implement 'I1.operator ++()' because one of them is not an operator.
// public class C3 : C2, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C3", "I1.operator ++()", "C3.op_IncrementAssignment()").WithLocation(15, 23)
);
}
[Fact]
public void Increment_100_Implement_RegularVsOperatorMismatch()
{
var source = @"
public interface I1
{
public void op_IncrementAssignment();
}
public class C2 : I1
{
void I1.op_IncrementAssignment()
{
System.Console.Write(""[C2.op_IncrementAssignment]"");
}
}
public class C3 : C2, I1
{
public virtual void operator ++()
{
System.Console.Write(""[C3.operator]"");
}
}
public class Program
{
static void Main()
{
I1 x = new C3();
x.op_IncrementAssignment();
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (15,23): error CS9311: 'C3' does not implement interface member 'I1.op_IncrementAssignment()'. 'C3.operator ++()' cannot implement 'I1.op_IncrementAssignment()' because one of them is not an operator.
// public class C3 : C2, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C3", "I1.op_IncrementAssignment()", "C3.operator ++()").WithLocation(15, 23)
);
}
[Fact]
public void Increment_101_Implement_RegularVsOperatorMismatch()
{
/*
public interface I1
{
public void operator++();
}
public class C1 : I1
{
public virtual void op_IncrementAssignment()
{
System.Console.Write(1);
}
}
*/
var ilSource = @"
.class interface public auto ansi abstract beforefieldinit I1
{
.method public hidebysig newslot abstract virtual specialname
instance void op_IncrementAssignment () cil managed
{
}
}
.class public auto ansi beforefieldinit C1
extends [mscorlib]System.Object
implements I1
{
.method public hidebysig newslot virtual
instance void op_IncrementAssignment () cil managed
{
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: call void [mscorlib]System.Console::Write(int32)
IL_0006: ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
}
}
";
var source1 =
@"
public class C2 : C1, I1
{
}
";
var compilation1 = CreateCompilationWithIL(source1, ilSource);
compilation1.VerifyDiagnostics(
// (2,23): error CS9311: 'C2' does not implement interface member 'I1.operator ++()'. 'C1.op_IncrementAssignment()' cannot implement 'I1.operator ++()' because one of them is not an operator.
// public class C2 : C1, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C2", "I1.operator ++()", "C1.op_IncrementAssignment()").WithLocation(2, 23)
);
var source2 =
@"
class Program
{
static void Main()
{
var c1 = new C1();
c1.op_IncrementAssignment();
I1 x = c1;
++x;
}
}
";
var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe);
CompileAndVerify(compilation2, expectedOutput: "11", verify: Verification.Skipped).VerifyDiagnostics();
var i1M1 = compilation1.GetTypeByMetadataName("I1").GetMembers().Single();
var c1 = compilation1.GetTypeByMetadataName("C1");
AssertEx.Equal("C1.op_IncrementAssignment()", c1.FindImplementationForInterfaceMember(i1M1).ToDisplayString());
}
[Fact]
public void Increment_102_Implement_RegularVsOperatorMismatch()
{
/*
public interface I1
{
public void op_IncrementAssignment();
}
public class C1 : I1
{
public virtual void operator++()
{
System.Console.Write(1);
}
}
*/
var ilSource = @"
.class interface public auto ansi abstract beforefieldinit I1
{
.method public hidebysig newslot abstract virtual
instance void op_IncrementAssignment () cil managed
{
}
}
.class public auto ansi beforefieldinit C1
extends [mscorlib]System.Object
implements I1
{
.method public hidebysig newslot virtual specialname
instance void op_IncrementAssignment () cil managed
{
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: call void [mscorlib]System.Console::Write(int32)
IL_0006: ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
}
}
";
var source1 =
@"
public class C2 : C1, I1
{
}
";
var compilation1 = CreateCompilationWithIL(source1, ilSource);
compilation1.VerifyDiagnostics(
// (2,23): error CS9311: 'C2' does not implement interface member 'I1.op_IncrementAssignment()'. 'C1.operator ++()' cannot implement 'I1.op_IncrementAssignment()' because one of them is not an operator.
// public class C2 : C1, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C2", "I1.op_IncrementAssignment()", "C1.operator ++()").WithLocation(2, 23)
);
var source2 =
@"
class Program
{
static void Main()
{
var c1 = new C1();
++c1;
I1 x = c1;
x.op_IncrementAssignment();
}
}
";
var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe);
CompileAndVerify(compilation2, expectedOutput: "11", verify: Verification.Skipped).VerifyDiagnostics();
var i1M1 = compilation1.GetTypeByMetadataName("I1").GetMembers().Single();
var c1 = compilation1.GetTypeByMetadataName("C1");
AssertEx.Equal("C1.operator ++()", c1.FindImplementationForInterfaceMember(i1M1).ToDisplayString());
}
[Fact]
public void Increment_103_Consumption_Implementation()
{
var source = @"
public interface I1
{
public void operator ++();
}
public class C1 : I1
{
public void operator ++()
{
System.Console.Write(""[C1.operator]"");
}
}
public class C2 : I1
{
void I1.operator ++()
{
System.Console.Write(""[C2.operator]"");
}
}
public class Program
{
static void Main()
{
I1 x = new C1();
++x;
x = new C2();
++x;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "[C1.operator][C2.operator]").VerifyDiagnostics();
}
[Fact]
public void Increment_104_Consumption_Overriding()
{
var source = @"
public abstract class C1
{
public abstract void operator ++();
}
public class C2 : C1
{
public override void operator ++()
{
System.Console.Write(""[C2.operator]"");
}
}
public class Program
{
static void Main()
{
C1 x = new C2();
++x;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "[C2.operator]").VerifyDiagnostics();
}
[Fact]
public void Increment_105_Shadow_RegularVsOperatorMismatch()
{
var source = @"
public class C1
{
public void operator ++(){}
public static C1 operator ++(C1 x) => x;
}
public class C2 : C1
{
public void op_IncrementAssignment(){}
public static C1 op_Increment(C1 x) => x;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void Increment_106_Shadow_RegularVsOperatorMismatch()
{
var source = @"
public class C2
{
public void op_IncrementAssignment(){}
public static C1 op_Increment(C1 x) => x;
}
public class C1 : C2
{
public void operator ++(){}
public static C1 operator ++(C1 x) => x;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyEmitDiagnostics();
}
[Theory]
[CombinatorialData]
public void Increment_107_CRef([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + op + @"()""/>.
/// </summary>
class C1
{
public static C1 operator " + op + @"(C1 x) => x;
public void operator " + op + @"() {}
}
/// <summary>
/// See <see cref=""C1.operator " + op + @"()""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator " + op + @"()", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_108_CRef([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + op + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
}
/// <summary>
/// See <see cref=""C1.operator " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator ++' that could not be resolved
// /// See <see cref="operator ++"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator " + op).WithArguments("operator " + op).WithLocation(3, 20),
// (11,20): warning CS1574: XML comment has cref attribute 'operator ++' that could not be resolved
// /// See <see cref="C1.operator ++"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator " + op).WithArguments("operator " + op).WithLocation(11, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_109_CRef([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + op + @"""/>.
/// </summary>
class C1
{
public static C1 operator " + op + @"(C1 x) => x;
}
/// <summary>
/// See <see cref=""C1.operator " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator " + op + @"(C1)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_110_CRef([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + op + @"""/>.
/// </summary>
class C1
{
public static C1 operator " + op + @"(C1 x) => x;
public void operator " + op + @"() {}
}
/// <summary>
/// See <see cref=""C1.operator " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator " + op + @"(C1)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_111_CRef([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + op + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
public static C1 operator " + op + @"(C1 x) => x;
}
/// <summary>
/// See <see cref=""C1.operator " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator " + op + @"(C1)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_112_CRef([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + op + @"(C1)""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
public static C1 operator " + op + @"(C1 x) => x;
}
/// <summary>
/// See <see cref=""C1.operator " + op + @"(C1)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator " + op + @"(C1)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_113_CRef([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + op + @"(C1)""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
}
/// <summary>
/// See <see cref=""C1.operator " + op + @"(C1)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator ++(C1)' that could not be resolved
// /// See <see cref="operator ++(C1)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator " + op + @"(C1)").WithArguments("operator " + op + @"(C1)").WithLocation(3, 20),
// (11,20): warning CS1574: XML comment has cref attribute 'operator ++(C1)' that could not be resolved
// /// See <see cref="C1.operator ++(C1)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator " + op + @"(C1)").WithArguments("operator " + op + @"(C1)").WithLocation(11, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void Increment_114_CRef([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + op + @"""/>.
/// </summary>
class C1
{
public static C1 " + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName) + @"() => null;
}
/// <summary>
/// See <see cref=""C1.operator " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator ++' that could not be resolved
// /// See <see cref="operator ++"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator " + op).WithArguments("operator " + op).WithLocation(3, 20),
// (11,20): warning CS1574: XML comment has cref attribute 'operator ++' that could not be resolved
// /// See <see cref="C1.operator ++"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator " + op).WithArguments("operator " + op).WithLocation(11, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void Increment_115_CRef([CombinatorialValues("++", "--")] string op)
{
string name = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
var source = @"
/// <summary>
/// See <see cref=""" + name + @"""/>.
/// </summary>
class C1
{
public static C1 operator " + op + @"(C1 x) => x;
}
/// <summary>
/// See <see cref=""C1." + name + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'op_IncrementAssignment' that could not be resolved
// /// See <see cref="op_IncrementAssignment"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, name).WithArguments(name).WithLocation(3, 20),
// (11,20): warning CS1574: XML comment has cref attribute 'op_IncrementAssignment' that could not be resolved
// /// See <see cref="C1.op_IncrementAssignment"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1." + name).WithArguments(name).WithLocation(11, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void Increment_116_CRef([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""" + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName) + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
}
/// <summary>
/// See <see cref=""C1." + (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator " + op + @"()", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_117_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + op + @"()""/>.
/// </summary>
class C1
{
public static C1 operator " + op + @"(C1 x) => x;
public static C1 operator checked " + op + @"(C1 x) => x;
public void operator " + op + @"() {}
public void operator checked " + op + @"() {}
}
/// <summary>
/// See <see cref=""C1.operator checked " + op + @"()""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator checked " + op + @"()", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_118_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + op + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
public void operator checked " + op + @"() {}
}
/// <summary>
/// See <see cref=""C1.operator checked " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator checked ++' that could not be resolved
// /// See <see cref="operator checked ++"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator checked " + op).WithArguments("operator checked " + op).WithLocation(3, 20),
// (12,20): warning CS1574: XML comment has cref attribute 'operator checked ++' that could not be resolved
// /// See <see cref="C1.operator checked ++"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + op).WithArguments("operator checked " + op).WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_119_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + op + @"""/>.
/// </summary>
class C1
{
public static C1 operator " + op + @"(C1 x) => x;
public static C1 operator checked " + op + @"(C1 x) => x;
}
/// <summary>
/// See <see cref=""C1.operator checked " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator checked " + op + @"(C1)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_120_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + op + @"""/>.
/// </summary>
class C1
{
public static C1 operator " + op + @"(C1 x) => x;
public static C1 operator checked " + op + @"(C1 x) => x;
public void operator " + op + @"() {}
public void operator checked " + op + @"() {}
}
#line 11
/// <summary>
/// See <see cref=""C1.operator checked " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator checked " + op + @"(C1)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_121_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + op + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
public void operator checked " + op + @"() {}
public static C1 operator " + op + @"(C1 x) => x;
public static C1 operator checked " + op + @"(C1 x) => x;
}
#line 11
/// <summary>
/// See <see cref=""C1.operator checked " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator checked " + op + @"(C1)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_122_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + op + @"(C1)""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
public void operator checked " + op + @"() {}
public static C1 operator " + op + @"(C1 x) => x;
public static C1 operator checked " + op + @"(C1 x) => x;
}
/// <summary>
/// See <see cref=""C1.operator checked " + op + @"(C1)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator checked " + op + @"(C1)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_123_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + op + @"(C1)""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
public void operator checked " + op + @"() {}
}
#line 10
/// <summary>
/// See <see cref=""C1.operator checked " + op + @"(C1)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator checked ++(C1)' that could not be resolved
// /// See <see cref="operator checked ++(C1)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator checked " + op + @"(C1)").WithArguments("operator checked " + op + @"(C1)").WithLocation(3, 20),
// (11,20): warning CS1574: XML comment has cref attribute 'operator ++(C1)' that could not be resolved
// /// See <see cref="C1.operator checked ++(C1)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + op + @"(C1)").WithArguments("operator checked " + op + @"(C1)").WithLocation(11, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void Increment_124_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + op + @"""/>.
/// </summary>
class C1
{
public static C1 " + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) + @"() => null;
}
/// <summary>
/// See <see cref=""C1.operator checked " + op + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator checked ++' that could not be resolved
// /// See <see cref="operator checked ++"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator checked " + op).WithArguments("operator checked " + op).WithLocation(3, 20),
// (11,20): warning CS1574: XML comment has cref attribute 'operator checked ++' that could not be resolved
// /// See <see cref="C1.operator checked ++"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + op).WithArguments("operator checked " + op).WithLocation(11, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void Increment_125_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
string name = (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName);
var source = @"
/// <summary>
/// See <see cref=""" + name + @"""/>.
/// </summary>
class C1
{
public static C1 operator " + op + @"(C1 x) => x;
public static C1 operator checked " + op + @"(C1 x) => x;
}
/// <summary>
/// See <see cref=""C1." + name + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'op_CheckedIncrementAssignment' that could not be resolved
// /// See <see cref="op_CheckedIncrementAssignment"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, name).WithArguments(name).WithLocation(3, 20),
// (12,20): warning CS1574: XML comment has cref attribute 'op_CheckedIncrementAssignment' that could not be resolved
// /// See <see cref="C1.op_CheckedIncrementAssignment"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1." + name).WithArguments(name).WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void Increment_126_CRef_Checked([CombinatorialValues("++", "--")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""" + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"() {}
public void operator checked " + op + @"() {}
}
/// <summary>
/// See <see cref=""C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator checked " + op + @"()", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void Increment_127_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct S1
{
public static readonly S1 operator " + op + @"(S1 s) => s;
public static readonly S1 operator checked " + op + @"(S1 s) => s;
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (4,40): error CS0106: The modifier 'readonly' is not valid for this item
// public static readonly S1 operator ++(S1 s) => s;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(4, 40),
// (5,48): error CS0106: The modifier 'readonly' is not valid for this item
// public static readonly S1 operator checked ++(S1 s) => s;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(5, 48)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>())
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_128_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct S1
{
public readonly void operator " + op + @"() {}
public readonly void operator checked " + op + @"() {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_129_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
readonly struct S1
{
public void operator " + op + @"() {}
public void operator checked " + op + @"() {}
}
readonly struct S2
{
public readonly void operator " + op + @"() {}
public readonly void operator checked " + op + @"() {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("S2").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_130_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
struct S1
{
int F;
public readonly void operator " + op + @"() { F++; }
public readonly void operator checked " + op + @"() { F++; }
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (5,42): error CS1604: Cannot assign to 'F' because it is read-only
// public readonly void operator ++() { F++; }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(5, 42),
// (6,50): error CS1604: Cannot assign to 'F' because it is read-only
// public readonly void operator checked ++() { F++; }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(6, 50)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_131_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
readonly struct S1
{
public void operator " + op + @"() { this = new S1(); }
public void operator checked " + op + @"() { this = new S1(); }
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (4,33): error CS1604: Cannot assign to 'this' because it is read-only
// public void operator ++() { this = new S1(); }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(4, 33),
// (5,41): error CS1604: Cannot assign to 'this' because it is read-only
// public void operator checked ++() { this = new S1(); }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(5, 41)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_132_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1<T> where T : I1<T>
{
static abstract T operator " + op + @"(T s);
static abstract T operator checked " + op + @"(T s);
}
struct S1 : I1<S1>
{
static readonly S1 I1<S1>.operator " + op + @"(S1 s) => s;
static readonly S1 I1<S1>.operator checked " + op + @"(S1 s) => s;
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
compilation.VerifyDiagnostics(
// (10,40): error CS0106: The modifier 'readonly' is not valid for this item
// static readonly S1 I1<S1>.operator ++(S1 s) => s;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(10, 40),
// (11,48): error CS0106: The modifier 'readonly' is not valid for this item
// static readonly S1 I1<S1>.operator checked ++(S1 s) => s;
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(11, 48)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_133_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"();
void operator checked " + op + @"();
}
struct S1 : I1
{
readonly void I1.operator " + op + @"() {}
readonly void I1.operator checked " + op + @"() {}
}
readonly struct S2 : I1
{
readonly void I1.operator " + op + @"() {}
readonly void I1.operator checked " + op + @"() {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("S2").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_134_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"();
void operator checked " + op + @"();
}
readonly struct S1 : I1
{
void I1.operator " + op + @"() {}
void I1.operator checked " + op + @"() {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_135_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"();
void operator checked " + op + @"();
}
struct S1 : I1
{
int F;
readonly void I1.operator " + op + @"() { F++; }
readonly void I1.operator checked " + op + @"() { F++; }
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (11,38): error CS1604: Cannot assign to 'F' because it is read-only
// readonly void I1.operator --() { F++; }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(11, 38),
// (12,46): error CS1604: Cannot assign to 'F' because it is read-only
// readonly void I1.operator checked --() { F++; }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(12, 46)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_136_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"();
void operator checked " + op + @"();
}
readonly struct S1 : I1
{
void I1.operator " + op + @"() { this = new S1(); }
void I1.operator checked " + op + @"() { this = new S1(); }
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (10,29): error CS1604: Cannot assign to 'this' because it is read-only
// void I1.operator ++() { this = new S1(); }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(10, 29),
// (11,37): error CS1604: Cannot assign to 'this' because it is read-only
// void I1.operator checked ++() { this = new S1(); }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(11, 37)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_137_Readonly([CombinatorialValues("++", "--")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"();
void operator checked " + op + @"();
}
interface I2 : I1
{
#line 200
readonly void I1.operator " + op + @"() {}
readonly void I1.operator checked " + op + @"() {}
}
class C3 : I1
{
#line 300
readonly void I1.operator " + op + @"() {}
readonly void I1.operator checked " + op + @"() {}
}
class C4
{
#line 400
public readonly void operator " + op + @"() {}
public readonly void operator checked " + op + @"() {}
}
interface I5
{
#line 500
readonly void operator " + op + @"();
readonly void operator checked " + op + @"();
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
compilation.VerifyDiagnostics(
// (200,31): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(200, 31),
// (201,39): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(201, 39),
// (300,31): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(300, 31),
// (301,39): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(301, 39),
// (400,35): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly void operator ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(400, 35),
// (401,43): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly void operator checked ++() {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(401, 43),
// (500,28): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void operator ++();
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(500, 28),
// (501,36): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void operator checked ++();
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(501, 36)
);
foreach (var m in compilation.GetTypeByMetadataName("I2").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("C3").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("C4").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("I5").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void Increment_138_VisualBasic([CombinatorialValues("++", "--")] string op)
{
var source1 = @"
public class C1
{
public void operator " + op + @"() {}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
var source2 = @"
Public Module Program
Public Sub Main()
Dim c1 = New C1()
c1" + op + @"
End Sub
End Module
";
CreateVisualBasicCompilation("Program", source2, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// error BC30800: Method arguments must be enclosed in parentheses.
Diagnostic(30800 /*ERRID.ERR_ObsoleteArgumentsNeedParens*/, op).WithLocation(5, 11),
// error BC30201: Expression expected.
Diagnostic(30201 /*ERRID.ERR_ExpectedExpression*/, "").WithLocation(5, 13),
// error BC30454: Expression is not a method.
Diagnostic(30454 /*ERRID.ERR_ExpectedProcedure*/, "c1").WithLocation(5, 9)
);
string opName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
var source3 = @"
Public Module Program
Public Sub Main()
Dim c1 = New C1()
c1." + opName + @"()
End Sub
End Module
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Public Overloads Sub op_IncrementAssignment()' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Public Overloads Sub " + opName + @"()", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 12)
);
}
[Theory]
[CombinatorialData]
public void Increment_139_VisualBasic([CombinatorialValues("++", "--")] string op)
{
var source1 = @"
public interface I1
{
public void operator " + op + @"();
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
string opName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
var source3 = @"
Public Class Program
Implements I1
Public Sub " + opName + @"() Implements I1." + opName + @"
End Sub
End Class
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Sub op_IncrementAssignment()' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Sub " + opName + @"()", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 16)
);
}
[Theory]
[CombinatorialData]
public void Increment_140_VisualBasic_Checked([CombinatorialValues("++", "--")] string op)
{
var source1 = @"
public interface I1
{
public sealed void operator " + op + @"(){}
public void operator checked " + op + @"();
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
string opName = (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName);
var source3 = @"
Public Class Program
Implements I1
Public Sub " + opName + @"() Implements I1." + opName + @"
End Sub
End Class
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Sub op_IncrementAssignment()' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Sub " + opName + @"()", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 16)
);
}
[Theory]
[CombinatorialData]
public void Increment_141_VisualBasic([CombinatorialValues("++", "--")] string op)
{
var source1 = @"
abstract public class C1
{
public abstract void operator " + op + @"();
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
string opName = (op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
var source3 = @"
Public Class Program
Inherits C1
Public Overrides Sub " + opName + @"()
End Sub
End Class
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Public MustOverride Overloads Sub op_IncrementAssignment()' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Public MustOverride Overloads Sub " + opName + @"()", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 26)
);
}
[Theory]
[CombinatorialData]
public void Increment_142_VisualBasic_Checked([CombinatorialValues("++", "--")] string op)
{
var source1 = @"
abstract public class C1
{
public void operator " + op + @"(){}
public abstract void operator checked " + op + @"();
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
string opName = (op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName);
var source3 = @"
Public Class Program
Inherits C1
Public Overrides Sub " + opName + @"()
End Sub
End Class
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Public MustOverride Overloads Sub op_IncrementAssignment()' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Public MustOverride Overloads Sub " + opName + @"()", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 26)
);
}
private static void AssertMetadataSymbol(MethodSymbol m, MethodKind kind, string display)
{
Assert.Equal(kind, m.MethodKind);
AssertEx.Equal(display, m.ToDisplayString());
Assert.False(m.HasUnsupportedMetadata);
Assert.True(m.HasSpecialName);
}
[Theory]
[CombinatorialData]
public void Increment_143_MetadataValidation([CombinatorialValues("++", "--")] string op, bool isChecked)
{
string name = isChecked ?
(op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) :
(op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
string staticName = isChecked ?
(op == "++" ? WellKnownMemberNames.CheckedIncrementOperatorName : WellKnownMemberNames.CheckedDecrementOperatorName) :
(op == "++" ? WellKnownMemberNames.IncrementOperatorName : WellKnownMemberNames.DecrementOperatorName);
var source1 = @"
using System.Runtime.CompilerServices;
public class C1
{
[SpecialName]
public void " + name + @"() {}
}
public class C2
{
[SpecialName]
public int " + name + @"() => 0; // Not void returning
}
public class C3
{
[SpecialName]
public void " + name + @"(int x = 0) {} // Has parameter
}
public class C4
{
[SpecialName]
public void " + name + @"(params int[] x) {} // Has params
}
public class C5
{
[SpecialName]
public void " + name + @"(__arglist) {} // Is vararg
}
public class C6
{
[SpecialName]
public void " + name + @"<T>() {} // Generic
}
public class C7
{
[SpecialName]
public void " + staticName + @"() {}
}
";
var comp1 = CreateCompilation(source1);
var comp1Ref = comp1.EmitToImageReference();
var comp2 = CreateCompilation("", references: [comp1Ref]);
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C1." + name),
MethodKind.UserDefinedOperator,
"C1.operator " + (isChecked ? "checked " : "") + op + "()");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C2." + name),
MethodKind.Ordinary,
"C2." + name + "()");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C3." + name),
MethodKind.Ordinary,
"C3." + name + "(int)");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C4." + name),
MethodKind.Ordinary,
"C4." + name + "(params int[])");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C5." + name),
MethodKind.Ordinary,
"C5." + name + "(__arglist)");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C6." + name),
MethodKind.Ordinary,
"C6." + name + "<T>()");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C7." + staticName),
MethodKind.Ordinary,
"C7." + staticName + "()");
}
[Theory]
[CombinatorialData]
public void Increment_144_MetadataValidation(
[CombinatorialValues("++", "--")] string op,
[CombinatorialValues("private", "protected", "private protected", "internal", "internal protected")] string accessibility,
bool isChecked)
{
string name = isChecked ?
(op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName) :
(op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName);
var source1 = @"
using System.Runtime.CompilerServices;
public class C1
{
[SpecialName]
" + accessibility + @" void " + name + @"() {}
}
";
var comp1 = CreateCompilation(source1);
var comp1Ref = comp1.EmitToImageReference();
var comp2 = CreateCompilation("", references: [comp1Ref], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C1." + name),
MethodKind.Ordinary,
"C1." + name + "()");
}
[Theory]
[CombinatorialData]
public void Increment_145_ExpressionTree([CombinatorialValues("++", "--")] string op)
{
var source = @"
using System.Linq.Expressions;
public class C1
{
public void operator" + op + @"()
{
}
}
public class Program
{
static void Main()
{
Expression<System.Action<C1>> x = (c1) => c1" + op + @";
}
}
";
var comp2 = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp2.VerifyDiagnostics(
// (15,51): error CS0832: An expression tree may not contain an assignment operator
// Expression<System.Action<C1>> x = (c1) => c1++;
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAssignment, "c1" + op).WithLocation(15, 51)
);
}
[Theory]
[CombinatorialData]
public void Increment_146_Partial([CombinatorialValues("++", "--")] string op)
{
var source = @"
partial class C1
{
public partial void operator" + op + @"();
public partial void M()
{
}
public partial void M();
}
";
var comp2 = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp2.VerifyDiagnostics(
// (4,20): error CS1519: Invalid token 'void' in a member declaration
// public partial void operator++();
Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "void").WithArguments("void").WithLocation(4, 20),
// (4,33): error CS9308: User-defined operator 'C1.operator ++()' must be declared public
// public partial void operator++();
Diagnostic(ErrorCode.ERR_OperatorsMustBePublic, op).WithArguments("C1.operator " + op + "()").WithLocation(4, 33),
// (4,33): error CS0501: 'C1.operator ++()' must declare a body because it is not marked abstract, extern, or partial
// public partial void operator++();
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("C1.operator " + op + "()").WithLocation(4, 33)
);
}
[Fact]
public void Increment_147_CopyModifiers()
{
/*
public class C1
{
public virtual void modopt(int64) operator ++() {}
}
*/
var ilSource = @"
.class public auto ansi beforefieldinit C1
extends System.Object
{
.method public hidebysig specialname newslot virtual
instance void modopt(int64) op_IncrementAssignment () cil managed
{
// Method begins at RVA 0x2069
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void System.Object::.ctor()
IL_0006: nop
IL_0007: ret
}
}
";
var source1 =
@"
public class C2 : C1
{
public override void operator ++()
{
System.Console.Write(""C2"");
}
static void Main()
{
C1 c1 = new C2();
c1++;
}
}
";
var compilation1 = CreateCompilationWithIL([source1, CompilerFeatureRequiredAttribute], ilSource, options: TestOptions.DebugExe);
CompileAndVerify(compilation1, symbolValidator: verify, sourceSymbolValidator: verify, expectedOutput: "C2").VerifyDiagnostics();
void verify(ModuleSymbol m)
{
AssertEx.Equal("void modopt(System.Int64) C2.op_IncrementAssignment()", m.GlobalNamespace.GetMember("C2.op_IncrementAssignment").ToTestDisplayString());
}
}
[Fact]
public void Increment_148_Consumption_InCatchFilter()
{
var source = @"
public struct C1
{
public int _F;
public void operator ++()
{
System.Console.Write(""++"");
_F++;
}
public void operator --()
{
System.Console.Write(""--"");
_F--;
}
public static implicit operator bool (C1 x) => x._F % 2 == 0;
}
public class Program
{
static void Main()
{
C1 x = new C1();
try
{
try
{
throw null;
}
catch when (++x)
{
System.Console.Write(""!"");
}
}
catch when (--x)
{
System.Console.Write(x._F);
}
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp, expectedOutput: "++--0").VerifyDiagnostics();
verifier.VerifyIL("Program.Main",
@"
{
// Code size 102 (0x66)
.maxstack 2
.locals init (C1 V_0, //x
bool V_1,
C1 V_2,
bool V_3)
IL_0000: nop
IL_0001: ldloca.s V_0
IL_0003: initobj ""C1""
.try
{
IL_0009: nop
.try
{
IL_000a: nop
IL_000b: ldnull
IL_000c: throw
}
filter
{
IL_000d: pop
IL_000e: ldloc.0
IL_000f: stloc.2
IL_0010: ldloca.s V_2
IL_0012: call ""void C1.op_IncrementAssignment()""
IL_0017: nop
IL_0018: ldloc.2
IL_0019: stloc.0
IL_001a: ldloc.2
IL_001b: call ""bool C1.op_Implicit(C1)""
IL_0020: stloc.1
IL_0021: ldloc.1
IL_0022: ldc.i4.0
IL_0023: cgt.un
IL_0025: endfilter
} // end filter
{ // handler
IL_0027: pop
IL_0028: nop
IL_0029: ldstr ""!""
IL_002e: call ""void System.Console.Write(string)""
IL_0033: nop
IL_0034: nop
IL_0035: leave.s IL_0037
}
IL_0037: nop
IL_0038: leave.s IL_0065
}
filter
{
IL_003a: pop
IL_003b: ldloc.0
IL_003c: stloc.2
IL_003d: ldloca.s V_2
IL_003f: call ""void C1.op_DecrementAssignment()""
IL_0044: nop
IL_0045: ldloc.2
IL_0046: stloc.0
IL_0047: ldloc.2
IL_0048: call ""bool C1.op_Implicit(C1)""
IL_004d: stloc.3
IL_004e: ldloc.3
IL_004f: ldc.i4.0
IL_0050: cgt.un
IL_0052: endfilter
} // end filter
{ // handler
IL_0054: pop
IL_0055: nop
IL_0056: ldloc.0
IL_0057: ldfld ""int C1._F""
IL_005c: call ""void System.Console.Write(int)""
IL_0061: nop
IL_0062: nop
IL_0063: leave.s IL_0065
}
IL_0065: ret
}
");
}
[Fact]
public void Increment_149_Consumption_ConditionalAccessTarget()
{
var source = @"
class C1
{
public int _F;
public void operator ++()
{
System.Console.Write(""++"");
_F++;
}
}
class C2
{
public C1 _F;
}
class Program
{
static void Test(C2 x)
{
x._F++;
++x._F;
x?._F++;
++x?._F;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (23,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer
// x?._F++;
Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "x?._F").WithLocation(23, 9),
// (24,11): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer
// ++x?._F;
Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "x?._F").WithLocation(24, 11)
);
}
[Theory]
[CombinatorialData]
public void Increment_150_GetOperatorKind([CombinatorialValues("++", "--")] string op)
{
SyntaxKind kind = SyntaxFactory.ParseToken(op).Kind();
string name = OperatorFacts.CompoundAssignmentOperatorNameFromSyntaxKind(kind, isChecked: false);
Assert.Equal(op == "++" ? WellKnownMemberNames.IncrementAssignmentOperatorName : WellKnownMemberNames.DecrementAssignmentOperatorName, name);
Assert.Equal(kind, SyntaxFacts.GetOperatorKind(name));
name = OperatorFacts.CompoundAssignmentOperatorNameFromSyntaxKind(kind, isChecked: true);
Assert.Equal(op == "++" ? WellKnownMemberNames.CheckedIncrementAssignmentOperatorName : WellKnownMemberNames.CheckedDecrementAssignmentOperatorName, name);
Assert.Equal(kind, SyntaxFacts.GetOperatorKind(name));
}
private static string CompoundAssignmentOperatorName(string op, bool isChecked = false)
{
SyntaxKind kind = CompoundAssignmentOperatorTokenKind(op);
return OperatorFacts.CompoundAssignmentOperatorNameFromSyntaxKind(kind, isChecked: isChecked);
}
private static SyntaxKind CompoundAssignmentOperatorTokenKind(string op)
{
return op switch
{
">>=" => SyntaxKind.GreaterThanGreaterThanEqualsToken,
">>>=" => SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
_ => SyntaxFactory.ParseToken(op).Kind(),
};
}
private static bool CompoundAssignmentOperatorHasCheckedForm(string op) => op is "+=" or "-=" or "*=" or "/=";
[Theory]
[CombinatorialData]
public void CompoundAssignment_00010([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
public void operator checked" + op + @"(C1 x) {}
";
}
var source =
typeKeyword + @" C1
{
public void operator" + op + @"(C1 x) {}
" + checkedForm + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net60);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net60);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.Regular13, targetFramework: TargetFramework.Net60);
comp.VerifyDiagnostics(
checkedForm is null ?
[
// (3,25): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// public void operator+=(C1 x) {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(3, 25)
] :
[
// (3,25): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// public void operator+=(C1 x) {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(3, 25),
// (5,33): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// public void operator checked+=(C1 x) {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(5, 33)
]
);
validate(comp.SourceModule);
comp = CreateCompilation(source, targetFramework: TargetFramework.Net60);
comp.VerifyDiagnostics(
checkedForm is null ?
[
// (3,25): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute..ctor'
// public void operator+=(C1 x) {}
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, op).WithArguments("System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute", ".ctor").WithLocation(3, 25)
] :
[
// (3,25): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute..ctor'
// public void operator+=(C1 x) {}
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, op).WithArguments("System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute", ".ctor").WithLocation(3, 25),
// (5,33): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute..ctor'
// public void operator checked+=(C1 x) {}
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, op).WithArguments("System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute", ".ctor").WithLocation(5, 33)
]
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
if (checkedForm is not null)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.Equal(typeKeyword == "interface", m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Empty(m.GetAttributes());
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00011_HasCheckedForm([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
Assert.True(CompoundAssignmentOperatorHasCheckedForm(op));
var source =
typeKeyword + @" C1
{
public void operator " + op + @"(C1 x) {}
public void operator checked" + op + @"(C1 x) {}
}
interface I1
{
public void operator " + op + @"(C1 x) {}
public void operator checked" + op + @"(C1 x) {}
}
" + typeKeyword + @" C2 : I1
{
void I1.operator " + op + @"(C1 x) {}
void I1.operator checked" + op + @"(C1 x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net60);
comp.VerifyEmitDiagnostics();
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00012_DoesNotHaveCheckedForm([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
Assert.False(CompoundAssignmentOperatorHasCheckedForm(op));
var source =
typeKeyword + @" C1
{
public void operator checked" + op + @"(C1 x) {}
}
interface I1
{
public void operator " + op + @"(C1 x) {}
}
" + typeKeyword + @" C2 : I1
{
void I1.operator checked" + op + @"(C1 x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net60);
comp.VerifyDiagnostics(
// (3,26): error CS9023: User-defined operator '%=' cannot be declared checked
// public void operator checked%=(C1 x) {}
Diagnostic(ErrorCode.ERR_OperatorCantBeChecked, "checked").WithArguments(op).WithLocation(3, 26),
// (13,22): error CS9023: User-defined operator '%=' cannot be declared checked
// void I1.operator checked%=(C1 x) {}
Diagnostic(ErrorCode.ERR_OperatorCantBeChecked, "checked").WithArguments(op).WithLocation(13, 22)
);
validateOp(comp.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.Equal(m.ContainingType.IsInterface, m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00013_Not_Static(
[CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op,
[CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
public static void operator checked" + op + @"(C1 x) {}
";
}
var source =
typeKeyword + @" C1
{
public static void operator" + op + @"(C1 x) {}
" + checkedForm + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net60);
comp.VerifyDiagnostics(
checkedForm is null ?
[
// (3,32): error CS0106: The modifier 'static' is not valid for this item
// public static void operator+=(C1 x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(3, 32)
] :
[
// (3,32): error CS0106: The modifier 'static' is not valid for this item
// public static void operator+=(C1 x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(3, 32),
// (5,40): error CS0106: The modifier 'static' is not valid for this item
// public static void operator checked+=(C1 x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(5, 40)
]
);
validate(comp.SourceModule);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
if (checkedForm is not null)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.Equal(typeKeyword == "interface", m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00014_NotInStaticClass([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
public void operator checked" + op + @"(int x) {}
";
}
var source = @"
static class C1
{
public void operator" + op + @"(int x) {}
" + checkedForm + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
checkedForm is null ?
[
// (4,25): error CS0715: 'C1.operator +=(int)': static classes cannot contain user-defined operators
// public void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OperatorInStaticClass, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 25)
] :
[
// (4,25): error CS0715: 'C1.operator +=(int)': static classes cannot contain user-defined operators
// public void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OperatorInStaticClass, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 25),
// (6,33): error CS0715: 'C1.operator checked +=(int)': static classes cannot contain user-defined operators
// public void operator checked+=(int x) {}
Diagnostic(ErrorCode.ERR_OperatorInStaticClass, op).WithArguments("C1.operator checked " + op + @"(int)").WithLocation(6, 33)
]
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00020_MustBePublic([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool structure, bool isChecked)
{
if (isChecked && !CompoundAssignmentOperatorHasCheckedForm(op))
{
return;
}
var source =
(structure ? "struct" : "class") + @" C1
{
void operator " + (isChecked ? "checked " : "") + op + @"(int x) {}
" + (isChecked ? "public void operator " + op + @"(int x) {}" : "") + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (3,19): error CS9308: User-defined operator 'C1.operator +=(int)' must be declared public
// void operator +=(int x) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBePublic, op).WithArguments("C1.operator " + (isChecked ? "checked " : "") + op + @"(int)").WithLocation(3, 19 + (isChecked ? 8 : 0))
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00021_ImplicitlyPublicInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
void operator checked" + op + @"(C1 x);
";
}
var source = @"
interface C1
{
void operator" + op + @"(C1 x);
" + checkedForm + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics();
validate(comp.SourceModule);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
if (checkedForm is not null)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00022_MustBePublic_ExplicitAccessibility(
[CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op,
[CombinatorialValues("struct", "class", "interface")] string typeKeyword,
[CombinatorialValues("private", "internal", "protected", "internal protected", "private protected")] string accessibility,
bool isChecked)
{
if (isChecked && !CompoundAssignmentOperatorHasCheckedForm(op))
{
return;
}
var source =
typeKeyword + @" C1
{
" + accessibility + @"
void operator " + (isChecked ? "checked " : "") + op + @"(int x) {}
" + (isChecked ? "public void operator " + op + @"(int x) {}" : "") + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net60);
comp.VerifyDiagnostics(
// (4,19): error CS9308: User-defined operator 'C1.operator +=(int)' must be declared public
// void operator +=(int x) {}
Diagnostic(ErrorCode.ERR_OperatorsMustBePublic, op).WithArguments("C1.operator " + (isChecked ? "checked " : "") + op + @"(int)").WithLocation(4, 19 + (isChecked ? 8 : 0))
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00030_MustReturnVoid([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public C1 operator " + op + @"(int x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,24): error CS9310: The return type for this operator must be void
// public C1 operator +=(int x) => throw null;
Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, op).WithLocation(3, 24)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00040_MustReturnVoid_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public C1 operator checked " + op + @"(int x) => throw null;
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,32): error CS9310: The return type for this operator must be void
// public C1 operator checked +=(int x) => throw null;
Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, op).WithLocation(3, 32),
// (3,32): error CS9025: The operator 'C1.operator checked +=(int)' requires a matching non-checked version of the operator to also be defined
// public C1 operator checked +=(int x) => throw null;
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "(int)").WithLocation(3, 32)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00050_WrongNumberOfParameters([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public void operator" + op + @"() {}
public void operator" + op + @"(C1 x, C1 y) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,25): error CS9313: Overloaded compound assignment operator '+=' takes one parameter
// public void operator+=() {}
Diagnostic(ErrorCode.ERR_BadCompoundAssignmentOpArgs, op).WithArguments(op).WithLocation(3, 25),
// (4,25): error CS1020: Overloadable binary operator expected
// public void operator+=(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, op).WithLocation(4, 25)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00060_WrongNumberOfParameters_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public void operator checked " + op + @"() {}
public void operator checked " + op + @"(C1 x, C1 y) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,34): error CS9313: Overloaded compound assignment operator '+=' takes one parameter
// public void operator checked +=() {}
Diagnostic(ErrorCode.ERR_BadCompoundAssignmentOpArgs, op).WithArguments(op).WithLocation(3, 34),
// (3,34): error CS9025: The operator 'C1.operator checked +=()' requires a matching non-checked version of the operator to also be defined
// public void operator checked +=() {}
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "()").WithLocation(3, 34),
// (4,34): error CS1020: Overloadable binary operator expected
// public void operator checked +=(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, op).WithLocation(4, 34),
// (4,34): error CS9025: The operator 'C1.operator checked +=(C1, C1)' requires a matching non-checked version of the operator to also be defined
// public void operator checked +=(C1 x, C1 y) {}
Diagnostic(ErrorCode.ERR_CheckedOperatorNeedsMatch, op).WithArguments("C1.operator checked " + op + "(C1, C1)").WithLocation(4, 34)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00090_AbstractAllowedInClassAndInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
public abstract void operator checked" + op + @"(int x);
";
}
var source =
typeKeyword + @" C1
{
public abstract void operator" + op + @"(int x);
" + checkedForm + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
if (checkedForm is not null)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
comp = CreateCompilation(["class C2 : C1 {}", source, CompilerFeatureRequiredAttribute]);
if (typeKeyword == "interface")
{
comp.VerifyDiagnostics(
checkedForm is null ?
[
// (1,12): error CS0535: 'C2' does not implement interface member 'C1.operator +=(int)'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "C1").WithArguments("C2", "C1.operator " + op + @"(int)").WithLocation(1, 12)
] :
[
// (1,12): error CS0535: 'C2' does not implement interface member 'C1.operator +=(int)'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "C1").WithArguments("C2", "C1.operator " + op + @"(int)").WithLocation(1, 12),
// (1,12): error CS0535: 'C2' does not implement interface member 'C1.operator checked +=(int)'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "C1").WithArguments("C2", "C1.operator checked " + op + @"(int)").WithLocation(1, 12)
]
);
}
else
{
comp.VerifyDiagnostics(
checkedForm is null ?
[
// (1,7): error CS0534: 'C2' does not implement inherited abstract member 'C1.operator +=(int)'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C2").WithArguments("C2", "C1.operator " + op + @"(int)").WithLocation(1, 7)
] :
[
// (1,7): error CS0534: 'C2' does not implement inherited abstract member 'C1.operator checked +=(int)'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C2").WithArguments("C2", "C1.operator checked " + op + @"(int)").WithLocation(1, 7),
// (1,7): error CS0534: 'C2' does not implement inherited abstract member 'C1.operator +=(int)'
// class C2 : C1 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C2").WithArguments("C2", "C1.operator " + op + @"(int)").WithLocation(1, 7)
]
);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00100_AbstractIsOptionalInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
public void operator checked" + op + @"(int x);
";
}
var source = @"
interface C1
{
public void operator" + op + @"(int x);
" + checkedForm + @"
}
";
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
if (checkedForm is not null)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00110_AbstractCanBeImplementedInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface I3 : I1
{
void I1.operator " + op + @"(int x) {}
}
";
bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op);
if (hasCheckedForm)
{
source += @"
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
interface I4 : I2
{
void I2.operator checked " + op + @"(int x) {}
}
class C : I3, I4
{}
";
}
else
{
source += @"
class C : I3
{}
";
}
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType<MethodSymbol>().Single(),
"void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)");
if (hasCheckedForm)
{
validateOp(
m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType<MethodSymbol>().Single(),
"void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)");
}
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00120_AbstractCanBeImplementedExplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source1 = @"
public interface I1
{
void operator " + op + @"(int x);
}
";
var source2 =
typeKeyword + @" C3 : I1
{
void I1.operator " + op + @"(int x) {}
}
";
bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op);
if (hasCheckedForm)
{
source1 += @"
public interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
";
source2 += @"
" + typeKeyword + @" C4 : I2
{
void I2.operator checked " + op + @"(int x) {}
}
";
}
var comp = CreateCompilation(source1 + source2, targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
var comp1 = CreateCompilation(source1, targetFramework: TargetFramework.Net90);
var comp2 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net90, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(
hasCheckedForm ?
[
// (3,22): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(3, 22),
// (8,30): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// void I2.operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(8, 30)
] :
[
// (3,22): error CS8652: The feature 'user-defined compound assignment operators' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("user-defined compound assignment operators").WithLocation(3, 22)
]
);
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()).Single(),
"void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)");
if (hasCheckedForm)
{
validateOp(
m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()).Single(),
"void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)");
}
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00130_AbstractCanBeImplementedImplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
" + typeKeyword + @" C3 : I1
{
public void operator " + op + @"(int x) {}
}
";
bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op);
if (hasCheckedForm)
{
source += @"
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C4 : I2
{
public void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
}
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType<MethodSymbol>().
Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: false)).Single());
if (hasCheckedForm)
{
validateOp(m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType<MethodSymbol>().
Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: true)).Single());
}
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
if (m is PEMethodSymbol)
{
Assert.True(m.IsMetadataVirtual());
Assert.True(m.IsMetadataFinal);
}
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00140_AbstractAllowedOnExplicitImplementationInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x) {}
}
interface I3 : I1
{
abstract void I1.operator " + op + @"(int x);
}
";
bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op);
if (hasCheckedForm)
{
source += @"
interface I2
{
void operator checked " + op + @"(int x) {}
sealed void operator " + op + @"(int x) {}
}
interface I4 : I2
{
abstract void I2.operator checked " + op + @"(int x);
}
";
}
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType<MethodSymbol>().Single(),
"void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)");
if (hasCheckedForm)
{
validateOp(
m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType<MethodSymbol>().Single(),
"void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)");
}
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.True(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
comp = CreateCompilation(["class C1 : I3 {}", source], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (1,12): error CS0535: 'C1' does not implement interface member 'I1.operator +=(int)'
// class C1 : I3 {}
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I3").WithArguments("C1", "I1.operator " + op + @"(int)").WithLocation(1, 12)
);
if (hasCheckedForm)
{
comp = CreateCompilation(["class C1 : I4 {}", source], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (1,12): error CS0535: 'C1' does not implement interface member 'I2.operator checked +=(int)'
// class C1 : I4 {}
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I4").WithArguments("C1", "I2.operator checked " + op + @"(int)").WithLocation(1, 12)
);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00150_AbstractCannotHaveBody([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
public abstract void operator checked" + op + @"(int x) {}
";
}
var source =
typeKeyword + @" C1
{
public abstract void operator" + op + @"(int x) {}
" + checkedForm + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
checkedForm is null ?
[
// (3,34): error CS0500: 'C1.operator +=(int)' cannot declare a body because it is marked abstract
// public abstract void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_AbstractHasBody, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 34)
] :
[
// (3,34): error CS0500: 'C1.operator +=(int)' cannot declare a body because it is marked abstract
// public abstract void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_AbstractHasBody, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 34),
// (5,42): error CS0500: 'C1.operator checked +=(int)' cannot declare a body because it is marked abstract
// public abstract void operator checked+=(int x) {}
Diagnostic(ErrorCode.ERR_AbstractHasBody, op).WithArguments("C1.operator checked " + op + @"(int)").WithLocation(5, 42)
]
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00160_AbstractExplicitImplementationCannotHaveBody([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator" + op + @"(int x);
}
interface I2 : I1
{
abstract void I1.operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (9,29): error CS0500: 'I2.I1.operator +=(int)' cannot declare a body because it is marked abstract
// abstract void I1.operator+=(int x) {}
Diagnostic(ErrorCode.ERR_AbstractHasBody, op).WithArguments("I2.I1.operator " + op + @"(int)").WithLocation(9, 29)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00161_AbstractExplicitImplementationCannotHaveBody_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface I1
{
sealed void operator" + op + @"(int x) {}
void operator checked" + op + @"(int x);
}
interface I2 : I1
{
abstract void I1.operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (10,37): error CS0500: 'I2.I1.operator checked +=(int)' cannot declare a body because it is marked abstract
// abstract void I1.operator checked+=(int x) {}
Diagnostic(ErrorCode.ERR_AbstractHasBody, op).WithArguments("I2.I1.operator checked " + op + @"(int)").WithLocation(10, 37)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00170_AbstractNotAllowedInNonAbstractClass([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("sealed", "")] string typeModifier)
{
var source = @"
" + typeModifier + @" class C1
{
public abstract void operator" + op + @"(int x);
}
";
bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op);
if (hasCheckedForm)
{
source +=
typeModifier + @" class C2
{
public abstract void operator checked " + op + @"(int x);
public void operator " + op + @"(int x) {}
}
";
}
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
hasCheckedForm ?
[
// (4,34): error CS0513: 'C1.operator +=(int)' is abstract but it is contained in non-abstract type 'C1'
// public abstract void operator+=(int x);
Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, op).WithArguments("C1.operator " + op + @"(int)", "C1").WithLocation(4, 34),
// (8,43): error CS0513: 'C2.operator checked +=(int)' is abstract but it is contained in non-abstract type 'C2'
// public abstract void operator checked +=(int x);
Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, op).WithArguments("C2.operator checked " + op + @"(int)", "C2").WithLocation(8, 43)
] :
[
// (4,34): error CS0513: 'C1.operator +=(int)' is abstract but it is contained in non-abstract type 'C1'
// public abstract void operator+=(int x);
Diagnostic(ErrorCode.ERR_AbstractInConcreteClass, op).WithArguments("C1.operator " + op + @"(int)", "C1").WithLocation(4, 34)
]
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00190_AbstractNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
struct C1
{
public abstract void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,34): error CS0106: The modifier 'abstract' is not valid for this item
// public abstract void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(4, 34)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00191_AbstractNotAllowedInStruct_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
struct C2
{
public abstract void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,43): error CS0106: The modifier 'abstract' is not valid for this item
// public abstract void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(4, 43)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00200_AbstractNotAllowedOnExplicitImplementationInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
" + typeKeyword + @" C3 : I1
{
abstract void I1.operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (9,31): error CS0106: The modifier 'abstract' is not valid for this item
// abstract void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(9, 31)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00201_AbstractNotAllowedOnExplicitImplementationInClassAndStruct_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "struct")] string typeKeyword)
{
var source = @"
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C4 : I2
{
abstract void I2.operator checked " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (10,39): error CS0106: The modifier 'abstract' is not valid for this item
// abstract void I2.operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(10, 39)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00210_VirtualAllowedInClassAndInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
public virtual void operator checked" + op + @"(int x) {}
";
}
var source =
typeKeyword + @" C1
{
public virtual void operator" + op + @"(int x) {}
" + checkedForm + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
if (checkedForm is not null)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00220_VirtualIsOptionalInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
void operator checked" + op + @"(int x) {}
";
}
var source = @"
interface C1
{
void operator" + op + @"(int x) {}
" + checkedForm + @"
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
if (checkedForm is not null)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00230_VirtualCanBeImplementedInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
virtual void operator " + op + @"(int x) {}
}
interface I3 : I1
{
void I1.operator " + op + @"(int x) {}
}
";
bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op);
if (hasCheckedForm)
{
source += @"
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
interface I4 : I2
{
void I2.operator checked " + op + @"(int x) {}
}
class C : I3, I4
{}
";
}
else
{
source += @"
class C : I3
{}
";
}
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType<MethodSymbol>().Single(),
"void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)");
if (hasCheckedForm)
{
validateOp(
m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType<MethodSymbol>().Single(),
"void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)");
}
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00240_VirtualCanBeImplementedExplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
virtual void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C3 : I1
{
void I1.operator " + op + @"(int x) {}
}
";
bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op);
if (hasCheckedForm)
{
source += @"
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C4 : I2
{
void I2.operator checked " + op + @"(int x) {}
}
";
}
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()).Single(),
"void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)");
if (hasCheckedForm)
{
validateOp(
m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()).Single(),
"void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)");
}
}
static void validateOp(MethodSymbol m, string implements)
{
Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.False(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Private, m.DeclaredAccessibility);
Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString());
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00250_VirtualCanBeImplementedImplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
virtual void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C3 : I1
{
public void operator " + op + @"(int x) {}
}
";
bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op);
if (hasCheckedForm)
{
source += @"
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C4 : I2
{
public void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
}
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType<MethodSymbol>().
Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: false)).Single());
if (hasCheckedForm)
{
validateOp(m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType<MethodSymbol>().
Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: true)).Single());
}
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
if (m is PEMethodSymbol)
{
Assert.True(m.IsMetadataVirtual());
Assert.True(m.IsMetadataFinal);
}
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00260_VirtualMustHaveBody([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public virtual void operator" + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,33): error CS0501: 'C1.operator +=(int)' must declare a body because it is not marked abstract, extern, or partial
// public virtual void operator+=(int x);
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 33)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00261_VirtualMustHaveBody_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public void operator" + op + @"(int x) {}
public virtual void operator checked" + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (4,41): error CS0501: 'C1.operator checked +=(int)' must declare a body because it is not marked abstract, extern, or partial
// public virtual void operator checked+=(int x);
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("C1.operator checked " + op + @"(int)").WithLocation(4, 41)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00270_VirtualNotAllowedInSealedClass([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
sealed class C1
{
public virtual void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,33): error CS0549: 'C1.operator +=(int x)' is a new virtual member in sealed type 'C1'
// public virtual void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_NewVirtualInSealed, op).WithArguments("C1.operator " + op + @"(int)", "C1").WithLocation(4, 33)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00271_VirtualNotAllowedInSealedClass_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
sealed class C2
{
#line 9
public virtual void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (9,42): error CS0549: 'C2.operator checked +=(int x)' is a new virtual member in sealed type 'C2'
// public virtual void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_NewVirtualInSealed, op).WithArguments("C2.operator checked " + op + @"(int)", "C2").WithLocation(9, 42)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00290_VirtualNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
struct C1
{
public virtual void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,33): error CS0106: The modifier 'virtual' is not valid for this item
// public virtual void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(4, 33)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00291_VirtualNotAllowedInStruct_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
struct C2
{
#line 9
public virtual void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (9,42): error CS0106: The modifier 'virtual' is not valid for this item
// public virtual void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(9, 42)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00300_VirtualNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
" + typeKeyword + @" C3 : I1
{
#line 15
virtual void I1.operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,30): error CS0106: The modifier 'virtual' is not valid for this item
// virtual void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(15, 30)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00301_VirtualNotAllowedOnExplicitImplementation_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C4 : I2
{
#line 20
virtual void I2.operator checked " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (20,38): error CS0106: The modifier 'virtual' is not valid for this item
// virtual void I2.operator checked +(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("virtual").WithLocation(20, 38)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00320_VirtualAbstractNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public virtual abstract void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (3,42): error CS0503: The abstract method 'C1.operator +=(int)' cannot be marked virtual
// public virtual abstract void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_AbstractNotVirtual, op).WithArguments("method", "C1.operator " + op + @"(int)").WithLocation(3, 42)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00321_VirtualAbstractNotAllowed_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public void operator" + op + @"(int x) {}
#line 4
public virtual abstract void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (4,50): error CS0503: The abstract method 'C1.operator checked +=(int)' cannot be marked virtual
// public virtual abstract void operator checked+=(int x) {}
Diagnostic(ErrorCode.ERR_AbstractNotVirtual, op).WithArguments("method", "C1.operator checked " + op + @"(int)").WithLocation(4, 50)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00330_OverrideAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
}
class C2 : C1
{
public override void operator" + op + @"(int x) {}
public override void operator checked" + op + @"(int x) {}
}
class C3 : C2
{
public override void operator" + op + @"(int x) {}
public override void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate1, sourceSymbolValidator: validate1).VerifyDiagnostics();
var source2 = @"
abstract class C1
{
public virtual void operator" + op + @"(int x) {}
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
}
class C2 : C1
{
public override void operator checked" + op + @"(int x) {}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp2, symbolValidator: validate2, sourceSymbolValidator: validate2).VerifyDiagnostics();
void validate1(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: true)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: true)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
void validate2(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: true)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
static void validateOp(MethodSymbol m, MethodSymbol overridden)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Same(overridden, m.OverriddenMethod);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00331_OverrideAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
}
class C2 : C1
{
public override void operator" + op + @"(int x) {}
}
class C3 : C2
{
public override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate1, sourceSymbolValidator: validate1).VerifyDiagnostics();
void validate1(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
static void validateOp(MethodSymbol m, MethodSymbol overridden)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Same(overridden, m.OverriddenMethod);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00340_AbstractOverrideAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
}
abstract class C2 : C1
{
public abstract override void operator" + op + @"(int x);
public abstract override void operator checked" + op + @"(int x);
}
class C3 : C2
{
public override void operator" + op + @"(int x) {}
public override void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: true)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: true)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
static void validateOp(MethodSymbol m, MethodSymbol overridden)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.Equal(m.ContainingType.Name == "C2", m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Same(overridden, m.OverriddenMethod);
}
comp = CreateCompilation(["class C4 : C2 {}", source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (1,7): error CS0534: 'C4' does not implement inherited abstract member 'C2.operator checked +=(int)'
// class C4 : C2 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C4").WithArguments("C4", "C2.operator checked " + op + @"(int)").WithLocation(1, 7),
// (1,7): error CS0534: 'C4' does not implement inherited abstract member 'C2.operator +=(int)'
// class C4 : C2 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C4").WithArguments("C4", "C2.operator " + op + @"(int)").WithLocation(1, 7)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00341_AbstractOverrideAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
}
abstract class C2 : C1
{
public abstract override void operator" + op + @"(int x);
}
class C3 : C2
{
public override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
static void validateOp(MethodSymbol m, MethodSymbol overridden)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.Equal(m.ContainingType.Name == "C2", m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Same(overridden, m.OverriddenMethod);
}
comp = CreateCompilation(["class C4 : C2 {}", source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (1,7): error CS0534: 'C4' does not implement inherited abstract member 'C2.operator +=(int)'
// class C4 : C2 {}
Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "C4").WithArguments("C4", "C2.operator " + op + @"(int)").WithLocation(1, 7)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00350_OverrideAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
struct S1
{
public override void operator" + op + @"(int x) {}
public override void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
validateOp(comp.GetMember<MethodSymbol>("S1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(comp.GetMember<MethodSymbol>("S1." + CompoundAssignmentOperatorName(op, isChecked: true)));
comp.VerifyDiagnostics(
// (4,34): error CS0115: 'S1.operator +=(int)': no suitable method found to override
// public override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("S1.operator " + op + @"(int)").WithLocation(4, 34),
// (5,42): error CS0115: 'S1.operator checked +=(int)': no suitable method found to override
// public override void operator checked+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("S1.operator checked " + op + @"(int)").WithLocation(5, 42)
);
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Null(m.OverriddenMethod);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00351_OverrideAllowedInStruct([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
struct S1
{
public override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
validateOp(comp.GetMember<MethodSymbol>("S1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(comp.GetMember<MethodSymbol>("S1." + CompoundAssignmentOperatorName(op, isChecked: true)));
comp.VerifyDiagnostics(
// (4,34): error CS0115: 'S1.operator +=(int)': no suitable method found to override
// public override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("S1.operator " + op + @"(int)").WithLocation(4, 34)
);
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Null(m.OverriddenMethod);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00370_OverrideNotAllowedInInterface([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface I1
{
public override void operator" + op + @"(int x) {}
}
interface I2
{
public override void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (4,34): error CS0106: The modifier 'override' is not valid for this item
// public override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(4, 34),
// (9,43): error CS0106: The modifier 'override' is not valid for this item
// public override void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(9, 43)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00371_OverrideNotAllowedInInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
public override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (4,34): error CS0106: The modifier 'override' is not valid for this item
// public override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(4, 34)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00380_OverrideNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C3 : I1
{
override void I1.operator " + op + @"(int x) {}
}
" + typeKeyword + @" C4 : I2
{
override void I2.operator checked " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,31): error CS0106: The modifier 'override' is not valid for this item
// override void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(15, 31),
// (20,39): error CS0106: The modifier 'override' is not valid for this item
// override void I2.operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(20, 39)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00381_OverrideNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
" + typeKeyword + @" C3 : I1
{
#line 15
override void I1.operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,31): error CS0106: The modifier 'override' is not valid for this item
// override void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("override").WithLocation(15, 31)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00400_VirtualOverrideNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
class C1
{
public virtual void operator" + op + @"(int x) {}
public virtual void operator checked" + op + @"(int x) {}
}
class C2 : C1
{
public virtual override void operator" + op + @"(int x) {}
public virtual override void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (10,42): error CS0113: A member 'C2.operator +=(int)' marked as override cannot be marked as new or virtual
// public virtual override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C2.operator " + op + @"(int)").WithLocation(10, 42),
// (11,50): error CS0113: A member 'C2.operator checked +=(int)' marked as override cannot be marked as new or virtual
// public virtual override void operator checked+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C2.operator checked " + op + @"(int)").WithLocation(11, 50)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00401_VirtualOverrideNotAllowed([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
class C1
{
public virtual void operator" + op + @"(int x) {}
}
class C2 : C1
{
#line 10
public virtual override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (10,42): error CS0113: A member 'C2.operator +=(int)' marked as override cannot be marked as new or virtual
// public virtual override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C2.operator " + op + @"(int)").WithLocation(10, 42)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00410_SealedAllowedInInterface([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface C1
{
sealed void operator" + op + @"(int x) {}
sealed void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
var source2 = @"
class C3 : C1
{
void C1.operator" + op + @"(int x) {}
void C1.operator checked" + op + @"(int x) {}
}
";
comp = CreateCompilation([source2, source], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (4,21): error CS0539: 'C3.operator +=(int)' in explicit interface declaration is not found among members of the interface that can be implemented
// void C1.operator++() {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C3.operator " + op + @"(int)").WithLocation(4, 21),
// (5,29): error CS0539: 'C3.operator checked +=(int)' in explicit interface declaration is not found among members of the interface that can be implemented
// void C1.operator checked++() {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C3.operator checked " + op + @"(int)").WithLocation(5, 29)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00411_SealedAllowedInInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface C1
{
sealed void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
var source2 = @"
class C3 : C1
{
void C1.operator" + op + @"(int x) {}
}
";
comp = CreateCompilation([source2, source], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (4,21): error CS0539: 'C3.operator +=(int)' in explicit interface declaration is not found among members of the interface that can be implemented
// void C1.operator++() {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, op).WithArguments("C3.operator " + op + @"(int)").WithLocation(4, 21)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00420_SealedOverrideAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
}
abstract class C2 : C1
{
public sealed override void operator" + op + @"(int x) {}
public sealed override void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: true)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
static void validateOp(MethodSymbol m, MethodSymbol overridden)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.True(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Same(overridden, m.OverriddenMethod);
}
var source2 = @"
class C3 : C2
{
public override void operator" + op + @"(int x) {}
public override void operator checked" + op + @"(int x) {}
}
";
comp = CreateCompilation([source2, source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,34): error CS0239: 'C3.operator +=(int)': cannot override inherited member 'C2.operator +=(int)' because it is sealed
// public override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_CantOverrideSealed, op).WithArguments("C3.operator " + op + @"(int)", "C2.operator " + op + @"(int)").WithLocation(4, 34),
// (5,42): error CS0239: 'C3.operator checked +=(int)': cannot override inherited member 'C2.operator checked +=(int)' because it is sealed
// public override void operator checked+=(int x) {}
Diagnostic(ErrorCode.ERR_CantOverrideSealed, op).WithArguments("C3.operator checked " + op + @"(int)", "C2.operator checked " + op + @"(int)").WithLocation(5, 42)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00421_SealedOverrideAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
}
abstract class C2 : C1
{
public sealed override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics();
void validate(ModuleSymbol m)
{
validateOp(
m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)),
m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
static void validateOp(MethodSymbol m, MethodSymbol overridden)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.True(m.IsSealed);
Assert.True(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
Assert.Same(overridden, m.OverriddenMethod);
}
var source2 = @"
class C3 : C2
{
public override void operator" + op + @"(int x) {}
}
";
comp = CreateCompilation([source2, source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,34): error CS0239: 'C3.operator +=(int)': cannot override inherited member 'C2.operator +=(int)' because it is sealed
// public override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_CantOverrideSealed, op).WithArguments("C3.operator " + op + @"(int)", "C2.operator " + op + @"(int)").WithLocation(4, 34)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00430_SealedNotAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
class C1
{
public sealed void operator" + op + @"(int x) {}
}
class C2
{
public sealed void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,32): error CS0238: 'C1.operator +=(int)' cannot be sealed because it is not an override
// public sealed void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 32),
// (9,41): error CS0238: 'C2.operator checked +=(int x)' cannot be sealed because it is not an override
// public sealed void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C2.operator checked " + op + @"(int)").WithLocation(9, 41)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00431_SealedNotAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
class C1
{
public sealed void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,32): error CS0238: 'C1.operator +=(int)' cannot be sealed because it is not an override
// public sealed void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 32)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00440_SealedNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
struct C1
{
public sealed void operator" + op + @"(int x) {}
}
struct C2
{
public sealed void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,32): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 32),
// (9,41): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(9, 41)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00441_SealedNotAllowedInStruct([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
struct C1
{
public sealed void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,32): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 32)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00450_SealedOverrideNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
struct C1
{
public sealed override void operator" + op + @"(int x) {}
}
struct C2
{
public sealed override void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,41): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 41),
// (4,41): error CS0115: 'C1.operator +=(int)': no suitable method found to override
// public sealed override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 41),
// (9,50): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed override void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(9, 50),
// (9,50): error CS0115: 'C2.operator checked +=(int)': no suitable method found to override
// public sealed override void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C2.operator checked " + op + @"(int)").WithLocation(9, 50)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00451_SealedOverrideNotAllowedInStruct([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
struct C1
{
public sealed override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,41): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 41),
// (4,41): error CS0115: 'C1.operator +=(int)': no suitable method found to override
// public sealed override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 41)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00460_SealedAbstractOverrideNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
struct C1
{
public sealed abstract override void operator" + op + @"(int x) {}
}
struct C2
{
public sealed abstract override void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,50): error CS0106: The modifier 'abstract' is not valid for this item
// public sealed abstract override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(4, 50),
// (4,50): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 50),
// (4,50): error CS0115: 'C1.operator +=(int)': no suitable method found to override
// public sealed abstract override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 50),
// (9,59): error CS0106: The modifier 'abstract' is not valid for this item
// public sealed abstract override void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(9, 59),
// (9,59): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract override void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(9, 59),
// (9,59): error CS0115: 'C2.operator checked +=(int)': no suitable method found to override
// public sealed abstract override void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C2.operator checked " + op + @"(int)").WithLocation(9, 59)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00461_SealedAbstractOverrideNotAllowedInStruct([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
struct C1
{
public sealed abstract override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,50): error CS0106: The modifier 'abstract' is not valid for this item
// public sealed abstract override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("abstract").WithLocation(4, 50),
// (4,50): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 50),
// (4,50): error CS0115: 'C1.operator +=(int)': no suitable method found to override
// public sealed abstract override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 50)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00470_SealedNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C3 : I1
{
sealed void I1.operator " + op + @"(int x) {}
}
" + typeKeyword + @" C4 : I2
{
sealed void I2.operator checked " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,29): error CS0106: The modifier 'sealed' is not valid for this item
// sealed void I1.operator +(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 29),
// (20,37): error CS0106: The modifier 'sealed' is not valid for this item
// sealed void I2.operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 37)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00471_SealedNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
" + typeKeyword + @" C3 : I1
{
#line 15
sealed void I1.operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,29): error CS0106: The modifier 'sealed' is not valid for this item
// sealed void I1.operator +(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 29)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00490_SealedAbstractNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public sealed abstract void operator" + op + @"(int x);
public sealed abstract void operator checked" + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
if (typeKeyword == "interface")
{
comp.VerifyDiagnostics(
// (3,41): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract void operator-=(int x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(3, 41),
// (4,49): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract void operator checked-=(int x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 49)
);
}
else
{
comp.VerifyDiagnostics(
// (3,41): error CS0238: 'C1.operator +=(int)' cannot be sealed because it is not an override
// public sealed abstract void operator+=(int x);
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 41),
// (4,49): error CS0238: 'C1.operator checked +=(int)' cannot be sealed because it is not an override
// public sealed abstract void operator checked+=(int x);
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator checked " + op + @"(int)").WithLocation(4, 49)
);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00491_SealedAbstractNotAllowed([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public sealed abstract void operator" + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
if (typeKeyword == "interface")
{
comp.VerifyDiagnostics(
// (3,41): error CS0106: The modifier 'sealed' is not valid for this item
// public sealed abstract void operator-=(int x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(3, 41)
);
}
else
{
comp.VerifyDiagnostics(
// (3,41): error CS0238: 'C1.operator +=(int)' cannot be sealed because it is not an override
// public sealed abstract void operator+=(int x);
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 41)
);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00500_SealedAbstractOverrideNotAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
}
abstract class C2 : C1
{
public sealed abstract override void operator" + op + @"(int x);
public sealed abstract override void operator checked" + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (10,50): error CS0502: 'C2.operator +=(int)' cannot be both abstract and sealed
// public sealed abstract override void operator+=(int x);
Diagnostic(ErrorCode.ERR_AbstractAndSealed, op).WithArguments("C2.operator " + op + @"(int)").WithLocation(10, 50),
// (11,58): error CS0502: 'C2.operator checked +=(int)' cannot be both abstract and sealed
// public sealed abstract override void operator checked+=(int x);
Diagnostic(ErrorCode.ERR_AbstractAndSealed, op).WithArguments("C2.operator checked " + op + @"(int)").WithLocation(11, 58)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00501_SealedAbstractOverrideNotAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool abstractInBase)
{
var source = @"
abstract class C1
{
public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @"
}
abstract class C2 : C1
{
public sealed abstract override void operator" + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (9,50): error CS0502: 'C2.operator +=(int)' cannot be both abstract and sealed
// public sealed abstract override void operator+=(int x);
Diagnostic(ErrorCode.ERR_AbstractAndSealed, op).WithArguments("C2.operator " + op + @"(int)").WithLocation(9, 50)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00510_SealedAbstractNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
interface I3 : I1
{
sealed abstract void I1.operator " + op + @"(int x);
}
interface I4 : I2
{
sealed abstract void I2.operator checked " + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,38): error CS0106: The modifier 'sealed' is not valid for this item
// sealed abstract void I1.operator +=(int x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 38),
// (20,46): error CS0106: The modifier 'sealed' is not valid for this item
// sealed abstract void I2.operator checked +=(int x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(20, 46)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00511_SealedAbstractNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface I3 : I1
{
#line 15
sealed abstract void I1.operator " + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,38): error CS0106: The modifier 'sealed' is not valid for this item
// sealed abstract void I1.operator +=(int x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(15, 38)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00530_SealedVirtualNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public virtual sealed void operator" + op + @"(int x) {}
public virtual sealed void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
if (typeKeyword == "interface")
{
comp.VerifyDiagnostics(
// (3,40): error CS0106: The modifier 'sealed' is not valid for this item
// public virtual sealed void operator+(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(3, 40),
// (4,48): error CS0106: The modifier 'sealed' is not valid for this item
// public virtual sealed void operator checked+(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(4, 48)
);
}
else
{
comp.VerifyDiagnostics(
// (3,40): error CS0238: 'C1.operator +(int)' cannot be sealed because it is not an override
// public virtual sealed void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 40),
// (4,48): error CS0238: 'C1.operator checked +=(int)' cannot be sealed because it is not an override
// public virtual sealed void operator checked+(int x) {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator checked " + op + @"(int)").WithLocation(4, 48)
);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00531_SealedVirtualNotAllowed([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public virtual sealed void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
if (typeKeyword == "interface")
{
comp.VerifyDiagnostics(
// (3,40): error CS0106: The modifier 'sealed' is not valid for this item
// public virtual sealed void operator+(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("sealed").WithLocation(3, 40)
);
}
else
{
comp.VerifyDiagnostics(
// (3,40): error CS0238: 'C1.operator +(int)' cannot be sealed because it is not an override
// public virtual sealed void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_SealedNonOverride, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 40)
);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00540_NewAllowedInInterface_WRN_NewRequired([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract", "virtual", "sealed")] string baseModifier)
{
var source = @"
interface C1
{
public " + baseModifier + @" void operator" + op + @"(int x) " + (baseModifier == "abstract" ? ";" : "{}") + @"
public " + baseModifier + @" void operator checked" + op + @"(int x) " + (baseModifier == "abstract" ? ";" : "{}") + @"
}
interface C2 : C1
{
public void operator" + op + @"(int x) {}
public void operator checked" + op + @"(int x) {}
}
interface C3 : C1
{
public new void operator" + op + @"(int x) {}
public new void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (10,25): warning CS0108: 'C2.operator +=(int)' hides inherited member 'C1.operator +=(int)'. Use the new keyword if hiding was intended.
// public void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25),
// (11,33): warning CS0108: 'C2.operator checked +=(int)' hides inherited member 'C1.operator checked +=(int)'. Use the new keyword if hiding was intended.
// public void operator checked+=(int x) {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator checked " + op + @"(int)", "C1.operator checked " + op + @"(int)").WithLocation(11, 33)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: true)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00541_NewAllowedInInterface_WRN_NewRequired([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract", "virtual", "sealed")] string baseModifier)
{
var source = @"
interface C1
{
public " + baseModifier + @" void operator" + op + @"(int x) " + (baseModifier == "abstract" ? ";" : "{}") + @"
}
interface C2 : C1
{
#line 10
public void operator" + op + @"(int x) {}
}
interface C3 : C1
{
public new void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (10,25): warning CS0108: 'C2.operator +=(int)' hides inherited member 'C1.operator +=(int)'. Use the new keyword if hiding was intended.
// public void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00550_NewAllowedInClass_WRN_NewRequired([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
class C1
{
public void operator" + op + @"(int x) {}
public void operator checked" + op + @"(int x) {}
}
class C2 : C1
{
public void operator" + op + @"(int x) {}
public void operator checked" + op + @"(int x) {}
}
class C3 : C1
{
public new void operator" + op + @"(int x) {}
public new void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(
// (10,25): warning CS0108: 'C2.operator +=(int)' hides inherited member 'C1.operator +=(int)'. Use the new keyword if hiding was intended.
// public void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25),
// (11,33): warning CS0108: 'C2.operator checked +=(int)' hides inherited member 'C1.operator checked +(int)'. Use the new keyword if hiding was intended.
// public void operator checked+=(int x) {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator checked " + op + @"(int)", "C1.operator checked " + op + @"(int)").WithLocation(11, 33)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: true)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00551_NewAllowedInClass_WRN_NewRequired([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
class C1
{
public void operator" + op + @"(int x) {}
}
class C2 : C1
{
#line 10
public void operator" + op + @"(int x) {}
}
class C3 : C1
{
public new void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(
// (10,25): warning CS0108: 'C2.operator +=(int)' hides inherited member 'C1.operator +=(int)'. Use the new keyword if hiding was intended.
// public void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewRequired, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00560_NewAllowedInClass_WRN_NewOrOverrideExpected([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
class C1
{
public virtual void operator" + op + @"(int x) {}
public virtual void operator checked" + op + @"(int x) {}
}
class C2 : C1
{
public void operator" + op + @"(int x) {}
public void operator checked" + op + @"(int x) {}
}
class C3 : C1
{
public new void operator" + op + @"(int x) {}
public new void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(
// (10,25): warning CS0114: 'C2.operator +=(int)' hides inherited member 'C1.operator +=(int)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25),
// (11,33): warning CS0114: 'C2.operator checked +=(int)' hides inherited member 'C1.operator checked +=(int)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator checked+=(int x) {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator checked " + op + @"(int)", "C1.operator checked " + op + @"(int)").WithLocation(11, 33)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: true)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00561_NewAllowedInClass_WRN_NewOrOverrideExpected([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
class C1
{
public virtual void operator" + op + @"(int x) {}
}
class C2 : C1
{
#line 10
public void operator" + op + @"(int x) {}
}
class C3 : C1
{
public new void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(
// (10,25): warning CS0114: 'C2.operator +=(int)' hides inherited member 'C1.operator +=(int)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C2." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C3." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00570_NewAllowedInClass_ERR_HidingAbstractMethod([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
abstract class C1
{
public abstract void operator" + op + @"(int x);
public abstract void operator checked" + op + @"(int x);
}
abstract class C2 : C1
{
public void operator" + op + @"(int x) {}
public void operator checked" + op + @"(int x) {}
}
abstract class C3 : C1
{
public new void operator" + op + @"(int x) {}
public new void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (10,25): error CS0533: 'C2.operator +=(int)' hides inherited abstract member 'C1.operator +=(int)'
// public void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25),
// (10,25): warning CS0114: 'C2.operator +=(int)' hides inherited member 'C1.operator +=(int)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25),
// (11,33): error CS0533: 'C2.operator checked +=(int)' hides inherited abstract member 'C1.operator checked +=(int)'
// public void operator checked+=(int x) {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C2.operator checked " + op + @"(int)", "C1.operator checked " + op + @"(int)").WithLocation(11, 33),
// (11,33): warning CS0114: 'C2.operator checked +=(int)' hides inherited member 'C1.operator checked +=(int)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator checked+=(int x) {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator checked " + op + @"(int)", "C1.operator checked " + op + @"(int)").WithLocation(11, 33),
// (16,29): error CS0533: 'C3.operator +=(int)' hides inherited abstract member 'C1.operator +=(int)'
// public new void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C3.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(16, 29),
// (17,37): error CS0533: 'C3.operator checked +=(int)' hides inherited abstract member 'C1.operator checked +=(int)'
// public new void operator checked+=(int x) {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C3.operator checked " + op + @"(int)", "C1.operator checked " + op + @"(int)").WithLocation(17, 37)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00571_NewAllowedInClass_ERR_HidingAbstractMethod([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
abstract class C1
{
public abstract void operator" + op + @"(int x);
}
abstract class C2 : C1
{
#line 10
public void operator" + op + @"(int x) {}
}
abstract class C3 : C1
{
#line 16
public new void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (10,25): error CS0533: 'C2.operator +=(int)' hides inherited abstract member 'C1.operator +=(int)'
// public void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25),
// (10,25): warning CS0114: 'C2.operator +=(int)' hides inherited member 'C1.operator +=(int)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
// public void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, op).WithArguments("C2.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(10, 25),
// (16,29): error CS0533: 'C3.operator +=(int)' hides inherited abstract member 'C1.operator +=(int)'
// public new void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_HidingAbstractMethod, op).WithArguments("C3.operator " + op + @"(int)", "C1.operator " + op + @"(int)").WithLocation(16, 29)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00580_NewAllowed_WRN_NewNotRequired([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public new void operator" + op + @"(int x) {}
public new void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (3,29): warning CS0109: The member 'C1.operator +=(int)' does not hide an accessible member. The new keyword is not required.
// public new void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 29),
// (4,37): warning CS0109: The member 'C1.operator checked +=(int)' does not hide an accessible member. The new keyword is not required.
// public new void operator checked+=(int x) {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator checked " + op + @"(int)").WithLocation(4, 37)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.Equal(typeKeyword == "interface", m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00581_NewAllowed_WRN_NewNotRequired([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public new void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (3,29): warning CS0109: The member 'C1.operator +=(int)' does not hide an accessible member. The new keyword is not required.
// public new void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 29)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.Equal(typeKeyword == "interface", m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00590_NewAbstractAllowedInClassAndInterface([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public new abstract void operator" + op + @"(int x);
public new abstract void operator checked" + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (3,38): warning CS0109: The member 'C1.operator +=(int)' does not hide an accessible member. The new keyword is not required.
// public new abstract void operator+=(int x);
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 38),
// (4,46): warning CS0109: The member 'C1.operator checked +=(int)' does not hide an accessible member. The new keyword is not required.
// public new abstract void operator checked+=(int x);
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator checked " + op + @"(int)").WithLocation(4, 46)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_005910_NewAbstractAllowedInClassAndInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public new abstract void operator" + op + @"(int x);
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (3,38): warning CS0109: The member 'C1.operator +=(int)' does not hide an accessible member. The new keyword is not required.
// public new abstract void operator+=(int x);
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 38)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.True(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00600_NewVirtualAllowedInClassAndInterface([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public new virtual void operator" + op + @"(int x) {}
public new virtual void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (3,37): warning CS0109: The member 'C1.operator +=(int)' does not hide an accessible member. The new keyword is not required.
// public new virtual void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 37),
// (4,45): warning CS0109: The member 'C1.operator checked +=(int)' does not hide an accessible member. The new keyword is not required.
// public new virtual void operator checked+=(int x) {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator checked " + op + @"(int)").WithLocation(4, 45)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00601_NewVirtualAllowedInClassAndInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword)
{
var source =
typeKeyword + @" C1
{
public new virtual void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (3,37): warning CS0109: The member 'C1.operator +=(int)' does not hide an accessible member. The new keyword is not required.
// public new virtual void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(3, 37)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.True(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00610_NewSealedAllowedInInterface([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface C1
{
sealed new void operator" + op + @"(int x) {}
sealed new void operator checked" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (4,29): warning CS0109: The member 'C1.operator +=(int)' does not hide an accessible member. The new keyword is not required.
// sealed new void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 29),
// (5,37): warning CS0109: The member 'C1.operator checked +=(int)' does not hide an accessible member. The new keyword is not required.
// sealed new void operator checked+=(int x) {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator checked " + op + @"(int)").WithLocation(5, 37)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: true)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00611_NewSealedAllowedInInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface C1
{
sealed new void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (4,29): warning CS0109: The member 'C1.operator +=(int)' does not hide an accessible member. The new keyword is not required.
// sealed new void operator+=(int x) {}
Diagnostic(ErrorCode.WRN_NewNotRequired, op).WithArguments("C1.operator " + op + @"(int)").WithLocation(4, 29)
);
void validate(ModuleSymbol m)
{
validateOp(m.GlobalNamespace.GetMember<MethodSymbol>("C1." + CompoundAssignmentOperatorName(op, isChecked: false)));
}
static void validateOp(MethodSymbol m)
{
Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind);
Assert.False(m.IsStatic);
Assert.False(m.IsAbstract);
Assert.False(m.IsVirtual);
Assert.False(m.IsSealed);
Assert.False(m.IsOverride);
Assert.True(m.HasSpecialName);
Assert.False(m.HasRuntimeSpecialName);
Assert.Equal(Accessibility.Public, m.DeclaredAccessibility);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00620_NewOverrideNotAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
class C1
{
public virtual void operator" + op + @"(int x) {}
}
class C2
{
public virtual void operator checked " + op + @"(int x) {}
public void operator " + op + @"(int x) {}
}
class C3 : C1
{
public new override void operator" + op + @"(int x) {}
}
class C4 : C2
{
public new override void operator checked " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (15,38): error CS0113: A member 'C3.operator +=(int)' marked as override cannot be marked as new or virtual
// public new override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C3.operator " + op + @"(int)").WithLocation(15, 38),
// (20,47): error CS0113: A member 'C4.operator checked +=(int)' marked as override cannot be marked as new or virtual
// public new override void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C4.operator checked " + op + @"(int)").WithLocation(20, 47)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00621_NewOverrideNotAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
class C1
{
public virtual void operator" + op + @"(int x) {}
}
class C3 : C1
{
#line 15
public new override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (15,38): error CS0113: A member 'C3.operator +=(int)' marked as override cannot be marked as new or virtual
// public new override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("C3.operator " + op + @"(int)").WithLocation(15, 38)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00630_NewOverrideNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
struct S1
{
public new override void operator" + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (4,38): error CS0113: A member 'S1.operator +=(int)' marked as override cannot be marked as new or virtual
// public new override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotNew, op).WithArguments("S1.operator " + op + @"(int)").WithLocation(4, 38),
// (4,38): error CS0115: 'S1.operator +=(int)': no suitable method found to override
// public new override void operator+=(int x) {}
Diagnostic(ErrorCode.ERR_OverrideNotExpected, op).WithArguments("S1.operator " + op + @"(int)").WithLocation(4, 38)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00650_NewNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C3 : I1
{
new void I1.operator " + op + @"(int x) {}
}
" + typeKeyword + @" C4 : I2
{
new void I2.operator checked " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,26): error CS0106: The modifier 'new' is not valid for this item
// new void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("new").WithLocation(15, 26),
// (20,34): error CS0106: The modifier 'new' is not valid for this item
// new void I2.operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("new").WithLocation(20, 34)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00651_NewNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
" + typeKeyword + @" C3 : I1
{
#line 15
new void I1.operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (15,26): error CS0106: The modifier 'new' is not valid for this item
// new void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("new").WithLocation(15, 26)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00670_ExplicitImplementationStaticVsInstanceMismatch([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
" + typeKeyword + @" C3
: I1
{
static void I1.operator " + op + @"(int x) {}
}
" + typeKeyword + @" C4
: I2
{
static void I2.operator checked " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (16,29): error CS0106: The modifier 'static' is not valid for this item
// static void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(16, 29),
// (22,37): error CS0106: The modifier 'static' is not valid for this item
// static void I2.operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(22, 37)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00671_ExplicitImplementationStaticVsInstanceMismatch([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
" + typeKeyword + @" C3
: I1
{
#line 16
static void I1.operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (16,29): error CS0106: The modifier 'static' is not valid for this item
// static void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(16, 29)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00680_ExplicitImplementationStaticVsInstanceMismatch([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface I2
{
void operator checked " + op + @"(int x);
sealed void operator " + op + @"(int x) {}
}
interface C3
: I1
{
static void I1.operator " + op + @"(int x) {}
}
interface C4
: I2
{
static void I2.operator checked " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (16,29): error CS0106: The modifier 'static' is not valid for this item
// static void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(16, 29),
// (22,37): error CS0106: The modifier 'static' is not valid for this item
// static void I2.operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(22, 37)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00681_ExplicitImplementationStaticVsInstanceMismatch([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface C3
: I1
{
#line 16
static void I1.operator " + op + @"(int x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp.VerifyDiagnostics(
// (16,29): error CS0106: The modifier 'static' is not valid for this item
// static void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("static").WithLocation(16, 29)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00690_Consumption_OnNonVariable([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool fromMetadata)
{
var source1 = @"
public class C1
{
public int _F;
public void operator" + op + @"(int x) => throw null;
public void operator checked" + op + @"(int x) => throw null;
public static C1 operator" + op[..^1] + @"(C1 x, int y)
{
System.Console.Write(""[operator]"");
return new C1() { _F = x._F + y };
}
public static C1 operator checked" + op[..^1] + @"(C1 x, int y)
{
System.Console.Write(""[operator checked]"");
checked
{
return new C1() { _F = x._F + y };
}
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static C1 P {get; set;} = new C1();
static void Main()
{
C1 x;
P" + op + @" 1;
System.Console.WriteLine(P._F);
x = P" + op + @" 1;
System.Console.WriteLine(P._F);
checked
{
P" + op + @" 1;
System.Console.WriteLine(P._F);
x = P" + op + @" 1;
System.Console.WriteLine(P._F);
}
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp2, expectedOutput: @"
[operator]1
[operator]2
[operator checked]3
[operator checked]4
").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00691_Consumption_OnNonVariable([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool fromMetadata)
{
var source1 = @"
public class C1
{
public int _F;
public void operator" + op + @"(int x) => throw null;
public static C1 operator" + op[..^1] + @"(C1 x, int y)
{
System.Console.Write(""[operator]"");
return new C1() { _F = x._F + y };
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static C1 P {get; set;} = new C1();
static void Main()
{
C1 x;
P" + op + @" 1;
System.Console.WriteLine(P._F);
x = P" + op + @" 1;
System.Console.WriteLine(P._F);
checked
{
P" + op + @" 1;
System.Console.WriteLine(P._F);
x = P" + op + @" 1;
System.Console.WriteLine(P._F);
}
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp2, expectedOutput: @"
[operator]1
[operator]2
[operator]3
[operator]4
").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00700_Consumption_NotUsed_Class([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(long x);
public void operator checked" + op + @"(long x);
}
public class C1 : I1
{
public int _F;
public void operator" + op + @"(long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
public void operator checked" + op + @"(long x)
{
System.Console.Write(""[operator checked]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
GetA(x)[Get0()]" + op + @" Get1();
}
static void Test2(C1[] x)
{
checked
{
GetA(x)[Get0()]" + op + @" Get1();
}
}
static void Test3<T>(T[] x) where T : class, I1
{
GetA(x)[Get0()]" + op + @" Get1();
}
static void Test4<T>(T[] x) where T : class, I1
{
checked
{
GetA(x)[Get0()]" + op + @" Get1();
}
}
static void Test5<T>(T[] x) where T : I1
{
GetA(x)[Get0()]" + op + @" Get1();
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
GetA(x)[Get0()]" + op + @" Get1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int Get1()
{
System.Console.Write(""[Get1]"");
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][Get1][operator]1
[GetA][Get0][Get1][operator checked]2
[GetA][Get0][Get1][operator]3
[GetA][Get0][Get1][operator checked]4
[GetA][Get0][Get1][operator]5
[GetA][Get0][Get1][operator checked]6
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 24 (0x18)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: call ""int Program.Get1()""
IL_0011: conv.i8
IL_0012: callvirt ""void C1." + methodName + @"(long)""
IL_0017: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 33 (0x21)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: box ""T""
IL_0015: call ""int Program.Get1()""
IL_001a: conv.i8
IL_001b: callvirt ""void I1." + methodName + @"(long)""
IL_0020: ret
}
");
verifier.VerifyIL("Program.Test5<T>(T[])",
@"
{
// Code size 60 (0x3c)
.maxstack 2
.locals init (T V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: ldloca.s V_0
IL_0014: initobj ""T""
IL_001a: ldloc.0
IL_001b: box ""T""
IL_0020: brtrue.s IL_002a
IL_0022: ldobj ""T""
IL_0027: stloc.0
IL_0028: ldloca.s V_0
IL_002a: call ""int Program.Get1()""
IL_002f: conv.i8
IL_0030: constrained. ""T""
IL_0036: callvirt ""void I1." + methodName + @"(long)""
IL_003b: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var typeInfo = model.GetTypeInfo(opNode.Left);
Assert.Equal("C1", typeInfo.Type.ToTestDisplayString());
Assert.Equal("C1", typeInfo.ConvertedType.ToTestDisplayString());
Assert.True(model.GetConversion(opNode.Left).IsIdentity);
typeInfo = model.GetTypeInfo(opNode.Right);
Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString());
Assert.Equal("System.Int64", typeInfo.ConvertedType.ToTestDisplayString());
Assert.True(model.GetConversion(opNode.Right).IsNumeric);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void C1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @" Get1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'Get1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.Get1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get1()')
Instance Receiver:
null
Arguments(0)
");
methodName = CompoundAssignmentOperatorName(op, isChecked: true);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 24 (0x18)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: call ""int Program.Get1()""
IL_0011: conv.i8
IL_0012: callvirt ""void C1." + methodName + @"(long)""
IL_0017: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 33 (0x21)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: box ""T""
IL_0015: call ""int Program.Get1()""
IL_001a: conv.i8
IL_001b: callvirt ""void I1." + methodName + @"(long)""
IL_0020: ret
}
");
verifier.VerifyIL("Program.Test6<T>(T[])",
@"
{
// Code size 60 (0x3c)
.maxstack 2
.locals init (T V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: ldloca.s V_0
IL_0014: initobj ""T""
IL_001a: ldloc.0
IL_001b: box ""T""
IL_0020: brtrue.s IL_002a
IL_0022: ldobj ""T""
IL_0027: stloc.0
IL_0028: ldloca.s V_0
IL_002a: call ""int Program.Get1()""
IL_002f: conv.i8
IL_0030: constrained. ""T""
IL_0036: callvirt ""void I1." + methodName + @"(long)""
IL_003b: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @", Checked) (OperatorMethod: void I1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @" Get1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Checked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'Get1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.Get1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get1()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "C1", "int").WithLocation(23, 9),
// (30,13): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "C1", "int").WithLocation(30, 13),
// (36,9): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "T", "int").WithLocation(36, 9),
// (43,13): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "T", "int").WithLocation(43, 13),
// (49,9): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "T", "int").WithLocation(49, 9),
// (56,13): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "T", "int").WithLocation(56, 13)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
private static Operations.BinaryOperatorKind CompoundAssignmentOperatorToBinaryOperatorKind(string op)
{
switch (op)
{
case "*=": return Operations.BinaryOperatorKind.Multiply;
case "/=": return Operations.BinaryOperatorKind.Divide;
case "%=": return Operations.BinaryOperatorKind.Remainder;
case "+=": return Operations.BinaryOperatorKind.Add;
case "-=": return Operations.BinaryOperatorKind.Subtract;
case ">>=": return Operations.BinaryOperatorKind.RightShift;
case ">>>=": return Operations.BinaryOperatorKind.UnsignedRightShift;
case "<<=": return Operations.BinaryOperatorKind.LeftShift;
case "&=": return Operations.BinaryOperatorKind.And;
case "|=": return Operations.BinaryOperatorKind.Or;
case "^=": return Operations.BinaryOperatorKind.ExclusiveOr;
default: throw ExceptionUtilities.UnexpectedValue(op);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00701_Consumption_NotUsed_Class([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(long x);
}
public class C1 : I1
{
public int _F;
public void operator" + op + @"(long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test2(C1[] x)
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static void Test3<T>(T[] x) where T : class, I1
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test4<T>(T[] x) where T : class, I1
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static void Test5<T>(T[] x) where T : I1
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[Get1]"");
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][Get1][operator]1
[GetA][Get0][Get1][operator]2
[GetA][Get0][Get1][operator]3
[GetA][Get0][Get1][operator]4
[GetA][Get0][Get1][operator]5
[GetA][Get0][Get1][operator]6
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 24 (0x18)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: call ""int Program.G1()""
IL_0011: conv.i8
IL_0012: callvirt ""void C1." + methodName + @"(long)""
IL_0017: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 33 (0x21)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: box ""T""
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: callvirt ""void I1." + methodName + @"(long)""
IL_0020: ret
}
");
verifier.VerifyIL("Program.Test5<T>(T[])",
@"
{
// Code size 60 (0x3c)
.maxstack 2
.locals init (T V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: ldloca.s V_0
IL_0014: initobj ""T""
IL_001a: ldloc.0
IL_001b: box ""T""
IL_0020: brtrue.s IL_002a
IL_0022: ldobj ""T""
IL_0027: stloc.0
IL_0028: ldloca.s V_0
IL_002a: call ""int Program.G1()""
IL_002f: conv.i8
IL_0030: constrained. ""T""
IL_0036: callvirt ""void I1." + methodName + @"(long)""
IL_003b: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void C1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 24 (0x18)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: call ""int Program.G1()""
IL_0011: conv.i8
IL_0012: callvirt ""void C1." + methodName + @"(long)""
IL_0017: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 33 (0x21)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: box ""T""
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: callvirt ""void I1." + methodName + @"(long)""
IL_0020: ret
}
");
verifier.VerifyIL("Program.Test6<T>(T[])",
@"
{
// Code size 60 (0x3c)
.maxstack 2
.locals init (T V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: ldloca.s V_0
IL_0014: initobj ""T""
IL_001a: ldloc.0
IL_001b: box ""T""
IL_0020: brtrue.s IL_002a
IL_0022: ldobj ""T""
IL_0027: stloc.0
IL_0028: ldloca.s V_0
IL_002a: call ""int Program.G1()""
IL_002f: conv.i8
IL_0030: constrained. ""T""
IL_0036: callvirt ""void I1." + methodName + @"(long)""
IL_003b: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void I1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Checked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(23, 9),
// (30,13): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(30, 13),
// (36,9): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(36, 9),
// (43,13): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(43, 13),
// (49,9): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(49, 9),
// (56,13): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(56, 13)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00702_Consumption_NotUsed_Class([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(in long x);
}
public class C1 : I1
{
public int _F;
public void operator" + op + @"(in long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test2(C1[] x)
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static void Test3<T>(T[] x) where T : class, I1
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test4<T>(T[] x) where T : class, I1
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static void Test5<T>(T[] x) where T : I1
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[Get1]"");
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][Get1][operator]1
[GetA][Get0][Get1][operator]2
[GetA][Get0][Get1][operator]3
[GetA][Get0][Get1][operator]4
[GetA][Get0][Get1][operator]5
[GetA][Get0][Get1][operator]6
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 27 (0x1b)
.maxstack 2
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: call ""int Program.G1()""
IL_0011: conv.i8
IL_0012: stloc.0
IL_0013: ldloca.s V_0
IL_0015: callvirt ""void C1." + methodName + @"(in long)""
IL_001a: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 36 (0x24)
.maxstack 2
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: box ""T""
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: stloc.0
IL_001c: ldloca.s V_0
IL_001e: callvirt ""void I1." + methodName + @"(in long)""
IL_0023: ret
}
");
verifier.VerifyIL("Program.Test5<T>(T[])",
@"
{
// Code size 63 (0x3f)
.maxstack 2
.locals init (T V_0,
long V_1)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: ldloca.s V_0
IL_0014: initobj ""T""
IL_001a: ldloc.0
IL_001b: box ""T""
IL_0020: brtrue.s IL_002a
IL_0022: ldobj ""T""
IL_0027: stloc.0
IL_0028: ldloca.s V_0
IL_002a: call ""int Program.G1()""
IL_002f: conv.i8
IL_0030: stloc.1
IL_0031: ldloca.s V_1
IL_0033: constrained. ""T""
IL_0039: callvirt ""void I1." + methodName + @"(in long)""
IL_003e: ret
}
");
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 27 (0x1b)
.maxstack 2
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: call ""int Program.G1()""
IL_0011: conv.i8
IL_0012: stloc.0
IL_0013: ldloca.s V_0
IL_0015: callvirt ""void C1." + methodName + @"(in long)""
IL_001a: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 36 (0x24)
.maxstack 2
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: box ""T""
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: stloc.0
IL_001c: ldloca.s V_0
IL_001e: callvirt ""void I1." + methodName + @"(in long)""
IL_0023: ret
}
");
verifier.VerifyIL("Program.Test6<T>(T[])",
@"
{
// Code size 63 (0x3f)
.maxstack 2
.locals init (T V_0,
long V_1)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: ldloca.s V_0
IL_0014: initobj ""T""
IL_001a: ldloc.0
IL_001b: box ""T""
IL_0020: brtrue.s IL_002a
IL_0022: ldobj ""T""
IL_0027: stloc.0
IL_0028: ldloca.s V_0
IL_002a: call ""int Program.G1()""
IL_002f: conv.i8
IL_0030: stloc.1
IL_0031: ldloca.s V_1
IL_0033: constrained. ""T""
IL_0039: callvirt ""void I1." + methodName + @"(in long)""
IL_003e: ret
}
");
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00710_Consumption_NotUsed_Struct([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(long x);
public void operator checked" + op + @"(long x);
}
public struct C1 : I1
{
public int _F;
public void operator" + op + @"(long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
public void operator checked" + op + @"(long x)
{
System.Console.Write(""[operator checked]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
GetA(x)[Get0()]" + op + @" Get1();
}
static void Test2(C1[] x)
{
checked
{
GetA(x)[Get0()]" + op + @" Get1();
}
}
static void Test3<T>(T[] x) where T : struct, I1
{
GetA(x)[Get0()]" + op + @" Get1();
}
static void Test4<T>(T[] x) where T : struct, I1
{
checked
{
GetA(x)[Get0()]" + op + @" Get1();
}
}
static void Test5<T>(T[] x) where T : I1
{
GetA(x)[Get0()]" + op + @" Get1();
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
GetA(x)[Get0()]" + op + @" Get1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int Get1()
{
System.Console.Write(""[Get1]"");
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][Get1][operator]1
[GetA][Get0][Get1][operator checked]2
[GetA][Get0][Get1][operator]3
[GetA][Get0][Get1][operator checked]4
[GetA][Get0][Get1][operator]5
[GetA][Get0][Get1][operator checked]6
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 28 (0x1c)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: call ""int Program.Get1()""
IL_0015: conv.i8
IL_0016: call ""void C1." + methodName + @"(long)""
IL_001b: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 36 (0x24)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: call ""int Program.Get1()""
IL_0017: conv.i8
IL_0018: constrained. ""T""
IL_001e: callvirt ""void I1." + methodName + @"(long)""
IL_0023: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void C1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @" Get1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'Get1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.Get1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get1()')
Instance Receiver:
null
Arguments(0)
");
methodName = CompoundAssignmentOperatorName(op, isChecked: true);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 28 (0x1c)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: call ""int Program.Get1()""
IL_0015: conv.i8
IL_0016: call ""void C1." + methodName + @"(long)""
IL_001b: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 36 (0x24)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: call ""int Program.Get1()""
IL_0017: conv.i8
IL_0018: constrained. ""T""
IL_001e: callvirt ""void I1." + methodName + @"(long)""
IL_0023: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @", Checked) (OperatorMethod: void I1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @" Get1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Checked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'Get1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.Get1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get1()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "C1", "int").WithLocation(23, 9),
// (30,13): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "C1", "int").WithLocation(30, 13),
// (36,9): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "T", "int").WithLocation(36, 9),
// (43,13): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "T", "int").WithLocation(43, 13),
// (49,9): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "T", "int").WithLocation(49, 9),
// (56,13): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= Get1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" Get1()").WithArguments(op, "T", "int").WithLocation(56, 13)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00711_Consumption_NotUsed_Struct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(long x);
}
public struct C1 : I1
{
public int _F;
public void operator" + op + @"(long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test2(C1[] x)
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static void Test3<T>(T[] x) where T : struct, I1
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test4<T>(T[] x) where T : struct, I1
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static void Test5<T>(T[] x) where T : I1
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[Get1]"");
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][Get1][operator]1
[GetA][Get0][Get1][operator]2
[GetA][Get0][Get1][operator]3
[GetA][Get0][Get1][operator]4
[GetA][Get0][Get1][operator]5
[GetA][Get0][Get1][operator]6
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 28 (0x1c)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: call ""int Program.G1()""
IL_0015: conv.i8
IL_0016: call ""void C1." + methodName + @"(long)""
IL_001b: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 36 (0x24)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: call ""int Program.G1()""
IL_0017: conv.i8
IL_0018: constrained. ""T""
IL_001e: callvirt ""void I1." + methodName + @"(long)""
IL_0023: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void C1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 28 (0x1c)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: call ""int Program.G1()""
IL_0015: conv.i8
IL_0016: call ""void C1." + methodName + @"(long)""
IL_001b: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 36 (0x24)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: call ""int Program.G1()""
IL_0017: conv.i8
IL_0018: constrained. ""T""
IL_001e: callvirt ""void I1." + methodName + @"(long)""
IL_0023: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("System.Void", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void I1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: System.Void) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Checked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(23, 9),
// (30,13): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(30, 13),
// (36,9): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(36, 9),
// (43,13): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(43, 13),
// (49,9): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(49, 9),
// (56,13): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(56, 13)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00712_Consumption_NotUsed_Struct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(in long x);
}
public struct C1 : I1
{
public int _F;
public void operator" + op + @"(in long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
Test2(x);
System.Console.WriteLine(x[0]._F);
Test3(x);
System.Console.WriteLine(x[0]._F);
Test4(x);
System.Console.WriteLine(x[0]._F);
Test5(x);
System.Console.WriteLine(x[0]._F);
Test6(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test2(C1[] x)
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static void Test3<T>(T[] x) where T : struct, I1
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test4<T>(T[] x) where T : struct, I1
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static void Test5<T>(T[] x) where T : I1
{
GetA(x)[Get0()]" + op + @" G1();
}
static void Test6<T>(T[] x) where T : I1
{
checked
{
GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[Get1]"");
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][Get1][operator]1
[GetA][Get0][Get1][operator]2
[GetA][Get0][Get1][operator]3
[GetA][Get0][Get1][operator]4
[GetA][Get0][Get1][operator]5
[GetA][Get0][Get1][operator]6
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 31 (0x1f)
.maxstack 2
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: call ""int Program.G1()""
IL_0015: conv.i8
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""void C1." + methodName + @"(in long)""
IL_001e: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 39 (0x27)
.maxstack 2
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: call ""int Program.G1()""
IL_0017: conv.i8
IL_0018: stloc.0
IL_0019: ldloca.s V_0
IL_001b: constrained. ""T""
IL_0021: callvirt ""void I1." + methodName + @"(in long)""
IL_0026: ret
}
");
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 31 (0x1f)
.maxstack 2
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: call ""int Program.G1()""
IL_0015: conv.i8
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""void C1." + methodName + @"(in long)""
IL_001e: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 39 (0x27)
.maxstack 2
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: readonly.
IL_000d: ldelema ""T""
IL_0012: call ""int Program.G1()""
IL_0017: conv.i8
IL_0018: stloc.0
IL_0019: ldloca.s V_0
IL_001b: constrained. ""T""
IL_0021: callvirt ""void I1." + methodName + @"(in long)""
IL_0026: ret
}
");
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00720_Consumption_Used_Class([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(long x);
public void operator checked" + op + @"(long x);
}
public class C1 : I1
{
public int _F;
public void operator" + op + @"(long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
public void operator checked" + op + @"(long x)
{
System.Console.Write(""[operator checked]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static C1[] x = [null];
static void Main()
{
var val = new C1();
x[0] = val;
C1 y = Test1(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test2(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test3(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test4(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test5(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test6(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
}
static C1 Test1(C1[] x)
{
#line 23
return GetA(x)[Get0()]" + op + @" G1();
}
static C1 Test2(C1[] x)
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test3<T>(T[] x) where T : class, I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test4<T>(T[] x) where T : class, I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test5<T>(T[] x) where T : I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test6<T>(T[] x) where T : I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[G1]"");
x[0] = null;
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][G1][operator]1True
[GetA][Get0][G1][operator checked]2True
[GetA][Get0][G1][operator]3True
[GetA][Get0][G1][operator checked]4True
[GetA][Get0][G1][operator]5True
[GetA][Get0][G1][operator checked]6True
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 25 (0x19)
.maxstack 3
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: dup
IL_000d: call ""int Program.G1()""
IL_0012: conv.i8
IL_0013: callvirt ""void C1." + methodName + @"(long)""
IL_0018: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 34 (0x22)
.maxstack 3
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: dup
IL_0011: box ""T""
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: callvirt ""void I1." + methodName + @"(long)""
IL_0021: ret
}
");
verifier.VerifyIL("Program.Test5<T>(T[])",
@"
{
// Code size 85 (0x55)
.maxstack 3
.locals init (T[] V_0,
int V_1,
T V_2,
long V_3,
T V_4)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: stloc.0
IL_0007: call ""int Program.Get0()""
IL_000c: stloc.1
IL_000d: ldloc.0
IL_000e: ldloc.1
IL_000f: ldelem ""T""
IL_0014: stloc.2
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: stloc.3
IL_001c: ldloca.s V_4
IL_001e: initobj ""T""
IL_0024: ldloc.s V_4
IL_0026: box ""T""
IL_002b: brtrue.s IL_003d
IL_002d: ldloca.s V_2
IL_002f: ldloc.3
IL_0030: constrained. ""T""
IL_0036: callvirt ""void I1." + methodName + @"(long)""
IL_003b: br.s IL_0053
IL_003d: ldloca.s V_2
IL_003f: ldloc.3
IL_0040: constrained. ""T""
IL_0046: callvirt ""void I1." + methodName + @"(long)""
IL_004b: ldloc.0
IL_004c: ldloc.1
IL_004d: ldloc.2
IL_004e: stelem ""T""
IL_0053: ldloc.2
IL_0054: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("C1", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void C1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: C1) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
methodName = CompoundAssignmentOperatorName(op, isChecked: true);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 25 (0x19)
.maxstack 3
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: dup
IL_000d: call ""int Program.G1()""
IL_0012: conv.i8
IL_0013: callvirt ""void C1." + methodName + @"(long)""
IL_0018: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 34 (0x22)
.maxstack 3
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: dup
IL_0011: box ""T""
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: callvirt ""void I1." + methodName + @"(long)""
IL_0021: ret
}
");
verifier.VerifyIL("Program.Test6<T>(T[])",
@"
{
// Code size 85 (0x55)
.maxstack 3
.locals init (T[] V_0,
int V_1,
T V_2,
long V_3,
T V_4)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: stloc.0
IL_0007: call ""int Program.Get0()""
IL_000c: stloc.1
IL_000d: ldloc.0
IL_000e: ldloc.1
IL_000f: ldelem ""T""
IL_0014: stloc.2
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: stloc.3
IL_001c: ldloca.s V_4
IL_001e: initobj ""T""
IL_0024: ldloc.s V_4
IL_0026: box ""T""
IL_002b: brtrue.s IL_003d
IL_002d: ldloca.s V_2
IL_002f: ldloc.3
IL_0030: constrained. ""T""
IL_0036: callvirt ""void I1." + methodName + @"(long)""
IL_003b: br.s IL_0053
IL_003d: ldloca.s V_2
IL_003f: ldloc.3
IL_0040: constrained. ""T""
IL_0046: callvirt ""void I1." + methodName + @"(long)""
IL_004b: ldloc.0
IL_004c: ldloc.1
IL_004d: ldloc.2
IL_004e: stelem ""T""
IL_0053: ldloc.2
IL_0054: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("T", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @", Checked) (OperatorMethod: void I1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: T) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Checked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,16): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(23, 16),
// (30,20): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(30, 20),
// (36,16): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(36, 16),
// (43,20): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(43, 20),
// (49,16): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(49, 16),
// (56,20): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(56, 20)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00721_Consumption_Used_Class([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(long x);
}
public class C1 : I1
{
public int _F;
public void operator" + op + @"(long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static C1[] x = [null];
static void Main()
{
var val = new C1();
x[0] = val;
C1 y = Test1(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test2(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test3(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test4(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test5(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test6(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
}
static C1 Test1(C1[] x)
{
#line 23
return GetA(x)[Get0()]" + op + @" G1();
}
static C1 Test2(C1[] x)
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test3<T>(T[] x) where T : class, I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test4<T>(T[] x) where T : class, I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test5<T>(T[] x) where T : I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test6<T>(T[] x) where T : I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[G1]"");
x[0] = null;
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][G1][operator]1True
[GetA][Get0][G1][operator]2True
[GetA][Get0][G1][operator]3True
[GetA][Get0][G1][operator]4True
[GetA][Get0][G1][operator]5True
[GetA][Get0][G1][operator]6True
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 25 (0x19)
.maxstack 3
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: dup
IL_000d: call ""int Program.G1()""
IL_0012: conv.i8
IL_0013: callvirt ""void C1." + methodName + @"(long)""
IL_0018: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 34 (0x22)
.maxstack 3
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: dup
IL_0011: box ""T""
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: callvirt ""void I1." + methodName + @"(long)""
IL_0021: ret
}
");
verifier.VerifyIL("Program.Test5<T>(T[])",
@"
{
// Code size 85 (0x55)
.maxstack 3
.locals init (T[] V_0,
int V_1,
T V_2,
long V_3,
T V_4)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: stloc.0
IL_0007: call ""int Program.Get0()""
IL_000c: stloc.1
IL_000d: ldloc.0
IL_000e: ldloc.1
IL_000f: ldelem ""T""
IL_0014: stloc.2
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: stloc.3
IL_001c: ldloca.s V_4
IL_001e: initobj ""T""
IL_0024: ldloc.s V_4
IL_0026: box ""T""
IL_002b: brtrue.s IL_003d
IL_002d: ldloca.s V_2
IL_002f: ldloc.3
IL_0030: constrained. ""T""
IL_0036: callvirt ""void I1." + methodName + @"(long)""
IL_003b: br.s IL_0053
IL_003d: ldloca.s V_2
IL_003f: ldloc.3
IL_0040: constrained. ""T""
IL_0046: callvirt ""void I1." + methodName + @"(long)""
IL_004b: ldloc.0
IL_004c: ldloc.1
IL_004d: ldloc.2
IL_004e: stelem ""T""
IL_0053: ldloc.2
IL_0054: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("C1", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void C1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: C1) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 25 (0x19)
.maxstack 3
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: dup
IL_000d: call ""int Program.G1()""
IL_0012: conv.i8
IL_0013: callvirt ""void C1." + methodName + @"(long)""
IL_0018: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 34 (0x22)
.maxstack 3
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: dup
IL_0011: box ""T""
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: callvirt ""void I1." + methodName + @"(long)""
IL_0021: ret
}
");
verifier.VerifyIL("Program.Test6<T>(T[])",
@"
{
// Code size 85 (0x55)
.maxstack 3
.locals init (T[] V_0,
int V_1,
T V_2,
long V_3,
T V_4)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: stloc.0
IL_0007: call ""int Program.Get0()""
IL_000c: stloc.1
IL_000d: ldloc.0
IL_000e: ldloc.1
IL_000f: ldelem ""T""
IL_0014: stloc.2
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: stloc.3
IL_001c: ldloca.s V_4
IL_001e: initobj ""T""
IL_0024: ldloc.s V_4
IL_0026: box ""T""
IL_002b: brtrue.s IL_003d
IL_002d: ldloca.s V_2
IL_002f: ldloc.3
IL_0030: constrained. ""T""
IL_0036: callvirt ""void I1." + methodName + @"(long)""
IL_003b: br.s IL_0053
IL_003d: ldloca.s V_2
IL_003f: ldloc.3
IL_0040: constrained. ""T""
IL_0046: callvirt ""void I1." + methodName + @"(long)""
IL_004b: ldloc.0
IL_004c: ldloc.1
IL_004d: ldloc.2
IL_004e: stelem ""T""
IL_0053: ldloc.2
IL_0054: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("T", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void I1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: T) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Checked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,16): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(23, 16),
// (30,20): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(30, 20),
// (36,16): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(36, 16),
// (43,20): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(43, 20),
// (49,16): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(49, 16),
// (56,20): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(56, 20)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00722_Consumption_Used_Class([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(in long x);
}
public class C1 : I1
{
public int _F;
public void operator" + op + @"(in long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static C1[] x = [null];
static void Main()
{
var val = new C1();
x[0] = val;
C1 y = Test1(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test2(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test3(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test4(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test5(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
x[0] = val;
y = Test6(x);
System.Console.Write(y._F);
System.Console.WriteLine((object)val == y && x[0] is null);
}
static C1 Test1(C1[] x)
{
#line 23
return GetA(x)[Get0()]" + op + @" G1();
}
static C1 Test2(C1[] x)
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test3<T>(T[] x) where T : class, I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test4<T>(T[] x) where T : class, I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test5<T>(T[] x) where T : I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test6<T>(T[] x) where T : I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[G1]"");
x[0] = null;
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][G1][operator]1True
[GetA][Get0][G1][operator]2True
[GetA][Get0][G1][operator]3True
[GetA][Get0][G1][operator]4True
[GetA][Get0][G1][operator]5True
[GetA][Get0][G1][operator]6True
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 28 (0x1c)
.maxstack 3
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: dup
IL_000d: call ""int Program.G1()""
IL_0012: conv.i8
IL_0013: stloc.0
IL_0014: ldloca.s V_0
IL_0016: callvirt ""void C1." + methodName + @"(in long)""
IL_001b: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 37 (0x25)
.maxstack 3
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: dup
IL_0011: box ""T""
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: stloc.0
IL_001d: ldloca.s V_0
IL_001f: callvirt ""void I1." + methodName + @"(in long)""
IL_0024: ret
}
");
verifier.VerifyIL("Program.Test5<T>(T[])",
@"
{
// Code size 87 (0x57)
.maxstack 3
.locals init (T[] V_0,
int V_1,
T V_2,
long V_3,
T V_4)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: stloc.0
IL_0007: call ""int Program.Get0()""
IL_000c: stloc.1
IL_000d: ldloc.0
IL_000e: ldloc.1
IL_000f: ldelem ""T""
IL_0014: stloc.2
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: stloc.3
IL_001c: ldloca.s V_4
IL_001e: initobj ""T""
IL_0024: ldloc.s V_4
IL_0026: box ""T""
IL_002b: brtrue.s IL_003e
IL_002d: ldloca.s V_2
IL_002f: ldloca.s V_3
IL_0031: constrained. ""T""
IL_0037: callvirt ""void I1." + methodName + @"(in long)""
IL_003c: br.s IL_0055
IL_003e: ldloca.s V_2
IL_0040: ldloca.s V_3
IL_0042: constrained. ""T""
IL_0048: callvirt ""void I1." + methodName + @"(in long)""
IL_004d: ldloc.0
IL_004e: ldloc.1
IL_004f: ldloc.2
IL_0050: stelem ""T""
IL_0055: ldloc.2
IL_0056: ret
}
");
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 28 (0x1c)
.maxstack 3
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem.ref
IL_000c: dup
IL_000d: call ""int Program.G1()""
IL_0012: conv.i8
IL_0013: stloc.0
IL_0014: ldloca.s V_0
IL_0016: callvirt ""void C1." + methodName + @"(in long)""
IL_001b: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 37 (0x25)
.maxstack 3
.locals init (long V_0)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelem ""T""
IL_0010: dup
IL_0011: box ""T""
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: stloc.0
IL_001d: ldloca.s V_0
IL_001f: callvirt ""void I1." + methodName + @"(in long)""
IL_0024: ret
}
");
verifier.VerifyIL("Program.Test6<T>(T[])",
@"
{
// Code size 87 (0x57)
.maxstack 3
.locals init (T[] V_0,
int V_1,
T V_2,
long V_3,
T V_4)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: stloc.0
IL_0007: call ""int Program.Get0()""
IL_000c: stloc.1
IL_000d: ldloc.0
IL_000e: ldloc.1
IL_000f: ldelem ""T""
IL_0014: stloc.2
IL_0015: call ""int Program.G1()""
IL_001a: conv.i8
IL_001b: stloc.3
IL_001c: ldloca.s V_4
IL_001e: initobj ""T""
IL_0024: ldloc.s V_4
IL_0026: box ""T""
IL_002b: brtrue.s IL_003e
IL_002d: ldloca.s V_2
IL_002f: ldloca.s V_3
IL_0031: constrained. ""T""
IL_0037: callvirt ""void I1." + methodName + @"(in long)""
IL_003c: br.s IL_0055
IL_003e: ldloca.s V_2
IL_0040: ldloca.s V_3
IL_0042: constrained. ""T""
IL_0048: callvirt ""void I1." + methodName + @"(in long)""
IL_004d: ldloc.0
IL_004e: ldloc.1
IL_004f: ldloc.2
IL_0050: stelem ""T""
IL_0055: ldloc.2
IL_0056: ret
}
");
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00730_Consumption_Used_Struct([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(long x);
public void operator checked" + op + @"(long x);
}
public struct C1 : I1
{
public int _F;
public void operator" + op + @"(long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
public void operator checked" + op + @"(long x)
{
System.Console.Write(""[operator checked]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static C1[] x = [new C1()];
static void Main()
{
C1 y = Test1(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test2(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test3(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test4(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test5(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test6(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
}
static C1 Test1(C1[] x)
{
#line 23
return GetA(x)[Get0()]" + op + @" G1();
}
static C1 Test2(C1[] x)
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test3<T>(T[] x) where T : struct, I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test4<T>(T[] x) where T : struct, I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test5<T>(T[] x) where T : I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test6<T>(T[] x) where T : I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[G1]"");
x[0] = new C1() { _F = -1 };
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][G1][operator]11
[GetA][Get0][G1][operator checked]22
[GetA][Get0][G1][operator]33
[GetA][Get0][G1][operator checked]44
[GetA][Get0][G1][operator]55
[GetA][Get0][G1][operator checked]66
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 44 (0x2c)
.maxstack 3
.locals init (C1 V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: dup
IL_0011: ldobj ""C1""
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""int Program.G1()""
IL_001e: conv.i8
IL_001f: call ""void C1." + methodName + @"(long)""
IL_0024: ldloc.0
IL_0025: stobj ""C1""
IL_002a: ldloc.0
IL_002b: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 48 (0x30)
.maxstack 3
.locals init (int V_0,
T V_1)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: stloc.0
IL_000c: dup
IL_000d: ldloc.0
IL_000e: ldelem ""T""
IL_0013: stloc.1
IL_0014: ldloca.s V_1
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: constrained. ""T""
IL_0022: callvirt ""void I1." + methodName + @"(long)""
IL_0027: ldloc.0
IL_0028: ldloc.1
IL_0029: stelem ""T""
IL_002e: ldloc.1
IL_002f: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("C1", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void C1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: C1) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
methodName = CompoundAssignmentOperatorName(op, isChecked: true);
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 44 (0x2c)
.maxstack 3
.locals init (C1 V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: dup
IL_0011: ldobj ""C1""
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""int Program.G1()""
IL_001e: conv.i8
IL_001f: call ""void C1." + methodName + @"(long)""
IL_0024: ldloc.0
IL_0025: stobj ""C1""
IL_002a: ldloc.0
IL_002b: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 48 (0x30)
.maxstack 3
.locals init (int V_0,
T V_1)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: stloc.0
IL_000c: dup
IL_000d: ldloc.0
IL_000e: ldelem ""T""
IL_0013: stloc.1
IL_0014: ldloca.s V_1
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: constrained. ""T""
IL_0022: callvirt ""void I1." + methodName + @"(long)""
IL_0027: ldloc.0
IL_0028: ldloc.1
IL_0029: stelem ""T""
IL_002e: ldloc.1
IL_002f: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("T", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @", Checked) (OperatorMethod: void I1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: T) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Checked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,16): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(23, 16),
// (30,20): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(30, 20),
// (36,16): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(36, 16),
// (43,20): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(43, 20),
// (49,16): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(49, 16),
// (56,20): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(56, 20)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00731_Consumption_Used_Struct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(long x);
}
public struct C1 : I1
{
public int _F;
public void operator" + op + @"(long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static C1[] x = [new C1()];
static void Main()
{
C1 y = Test1(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test2(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test3(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test4(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test5(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test6(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
}
static C1 Test1(C1[] x)
{
#line 23
return GetA(x)[Get0()]" + op + @" G1();
}
static C1 Test2(C1[] x)
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test3<T>(T[] x) where T : struct, I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test4<T>(T[] x) where T : struct, I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test5<T>(T[] x) where T : I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test6<T>(T[] x) where T : I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[G1]"");
x[0] = new C1() { _F = -1 };
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][G1][operator]11
[GetA][Get0][G1][operator]22
[GetA][Get0][G1][operator]33
[GetA][Get0][G1][operator]44
[GetA][Get0][G1][operator]55
[GetA][Get0][G1][operator]66
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 44 (0x2c)
.maxstack 3
.locals init (C1 V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: dup
IL_0011: ldobj ""C1""
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""int Program.G1()""
IL_001e: conv.i8
IL_001f: call ""void C1." + methodName + @"(long)""
IL_0024: ldloc.0
IL_0025: stobj ""C1""
IL_002a: ldloc.0
IL_002b: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 48 (0x30)
.maxstack 3
.locals init (int V_0,
T V_1)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: stloc.0
IL_000c: dup
IL_000d: ldloc.0
IL_000e: ldelem ""T""
IL_0013: stloc.1
IL_0014: ldloca.s V_1
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: constrained. ""T""
IL_0022: callvirt ""void I1." + methodName + @"(long)""
IL_0027: ldloc.0
IL_0028: ldloc.1
IL_0029: stelem ""T""
IL_002e: ldloc.1
IL_002f: ret
}
");
var tree = comp2.SyntaxTrees.First();
var model = comp2.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void C1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("C1", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
var iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void C1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: C1) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: C1) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (C1[] Program.GetA<C1>(C1[] x)) (OperationKind.Invocation, Type: C1[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C1[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 44 (0x2c)
.maxstack 3
.locals init (C1 V_0)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: dup
IL_0011: ldobj ""C1""
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""int Program.G1()""
IL_001e: conv.i8
IL_001f: call ""void C1." + methodName + @"(long)""
IL_0024: ldloc.0
IL_0025: stobj ""C1""
IL_002a: ldloc.0
IL_002b: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 48 (0x30)
.maxstack 3
.locals init (int V_0,
T V_1)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: stloc.0
IL_000c: dup
IL_000d: ldloc.0
IL_000e: ldelem ""T""
IL_0013: stloc.1
IL_0014: ldloca.s V_1
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: constrained. ""T""
IL_0022: callvirt ""void I1." + methodName + @"(long)""
IL_0027: ldloc.0
IL_0028: ldloc.1
IL_0029: stelem ""T""
IL_002e: ldloc.1
IL_002f: ret
}
");
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Where(n => n.OperatorToken.Text == op).Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("void I1." + methodName + "(System.Int64 x)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
Assert.Equal("T", model.GetTypeInfo(opNode).Type.ToTestDisplayString());
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
iOp = model.GetOperation(opNode);
VerifyOperationTree(comp2, iOp, @"
ICompoundAssignmentOperation (BinaryOperatorKind." + CompoundAssignmentOperatorToBinaryOperatorKind(op) + @") (OperatorMethod: void I1." + methodName + @"(System.Int64 x)) (OperationKind.CompoundAssignment, Type: T) (Syntax: 'GetA(x)[Get0()]" + op + @" G1()')
InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Left:
IArrayElementReferenceOperation (OperationKind.ArrayElementReference, Type: T) (Syntax: 'GetA(x)[Get0()]')
Array reference:
IInvocationOperation (T[] Program.GetA<T>(T[] x)) (OperationKind.Invocation, Type: T[]) (Syntax: 'GetA(x)')
Instance Receiver:
null
Arguments(1):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: 'x')
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x')
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)
Indices(1):
IInvocationOperation (System.Int32 Program.Get0()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get0()')
Instance Receiver:
null
Arguments(0)
Right:
IConversionOperation (TryCast: False, Checked) (OperationKind.Conversion, Type: System.Int64, IsImplicit) (Syntax: 'G1()')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
Operand:
IInvocationOperation (System.Int32 Program.G1()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'G1()')
Instance Receiver:
null
Arguments(0)
");
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext);
verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13);
var expectedErrors = new[] {
// (23,16): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(23, 16),
// (30,20): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "C1", "int").WithLocation(30, 20),
// (36,16): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(36, 16),
// (43,20): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(43, 20),
// (49,16): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(49, 16),
// (56,20): error CS0019: Operator '+=' cannot be applied to operands of type 'T' and 'int'
// return GetA(x)[Get0()]+= G1();
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()]" + op + @" G1()").WithArguments(op, "T", "int").WithLocation(56, 20)
};
comp2.VerifyDiagnostics(expectedErrors);
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
comp2.VerifyDiagnostics(expectedErrors);
}
[Fact]
public void CompoundAssignment_00740_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(C right) {}
public void X(C right) {}
public C M1(C c, C c1)
{
c += c1;
return c;
}
public C M2(C c, C c1)
{
c.X(c1);
return c;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_00741_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(C right) {}
public void X(C right) {}
public C M1(C c, scoped C c1)
{
c += c1;
return c;
}
public C M2(C c, scoped C c1)
{
c.X(c1);
return c;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics(
// (7,9): error CS8350: This combination of arguments to 'C.operator +=(C)' is disallowed because it may expose variables referenced by parameter 'right' outside of their declaration scope
// c += c1;
Diagnostic(ErrorCode.ERR_CallArgMixing, "c += c1").WithArguments("C.operator +=(C)", "right").WithLocation(7, 9),
// (7,14): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope
// c += c1;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(7, 14),
// (12,9): error CS8350: This combination of arguments to 'C.X(C)' is disallowed because it may expose variables referenced by parameter 'right' outside of their declaration scope
// c.X(c1);
Diagnostic(ErrorCode.ERR_CallArgMixing, "c.X(c1)").WithArguments("C.X(C)", "right").WithLocation(12, 9),
// (12,13): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope
// c.X(c1);
Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(12, 13)
);
}
[Fact]
public void CompoundAssignment_00742_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(C right) {}
public void X(C right) {}
public C M1(scoped C c, C c1)
{
c += c1;
return c;
}
public C M2(scoped C c, C c1)
{
c.X(c1);
return c;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics(
// (8,16): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return c;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(8, 16),
// (13,16): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return c;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(13, 16)
);
}
[Fact]
public void CompoundAssignment_00743_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(C right) {}
public void X(C right) {}
public C M1(scoped C c, scoped C c1)
{
c += c1;
return c;
}
public C M2(scoped C c, scoped C c1)
{
c.X(c1);
return c;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics(
// (8,16): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return c;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(8, 16),
// (13,16): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return c;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(13, 16)
);
}
[Fact]
public void CompoundAssignment_00744_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(scoped C right) {}
public void X(scoped C right) {}
public C M1(C c, scoped C c1)
{
c += c1;
return c;
}
public C M2(C c, scoped C c1)
{
c.X(c1);
return c;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_00745_Consumption_RefSafety()
{
var source = """
using System.Diagnostics.CodeAnalysis;
public ref struct C
{
[UnscopedRef]
public void operator +=(C right) {}
[UnscopedRef]
public void X(C right) {}
public C M1(scoped C c, C c1)
{
c += c1;
#line 8
return c;
}
public C M2(scoped C c, C c1)
{
c.X(c1);
return c;
}
}
""";
CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(
// (8,16): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return c;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(8, 16),
// (13,16): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return c;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(13, 16)
);
}
[Fact]
public void CompoundAssignment_00746_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(C right) {}
public C M1(C c, C c1)
{
return c += c1;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_00747_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(C right) {}
public C M1(scoped C c, C c1)
{
return c += c1;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics(
// (6,16): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return c += c1;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(6, 16)
);
}
[Fact]
public void CompoundAssignment_00748_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(C right) {}
public C M1(C c, scoped C c1)
{
return c += c1;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics(
// (6,16): error CS8350: This combination of arguments to 'C.operator +=(C)' is disallowed because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return c += c1;
Diagnostic(ErrorCode.ERR_CallArgMixing, "c += c1").WithArguments("C.operator +=(C)", "right").WithLocation(6, 16),
// (6,21): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope
// return c += c1;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(6, 21)
);
}
[Fact]
public void CompoundAssignment_00749_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(scoped C right) {}
public C M1(C c, scoped C c1)
{
return c += c1;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_00750_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(C right) {}
public C M1(scoped C c, scoped C c1)
{
return c += c1;
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics(
// (6,16): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return c += c1;
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(6, 16)
);
}
[Fact]
public void CompoundAssignment_00751_Consumption_RefSafety()
{
var source = """
public ref struct C
{
public void operator +=(C right) {}
static C X(C c) => throw null;
public C M1(scoped C c, scoped C c1)
{
return X(c += c1);
}
}
""";
CreateCompilation([source, CompilerFeatureRequiredAttribute]).VerifyDiagnostics(
// (7,16): error CS8347: Cannot use a result of 'C.X(C)' in this context because it may expose variables referenced by parameter 'c' outside of their declaration scope
// return X(c += c1);
Diagnostic(ErrorCode.ERR_EscapeCall, "X(c += c1)").WithArguments("C.X(C)", "c").WithLocation(7, 16),
// (7,18): error CS8352: Cannot use variable 'scoped C c' in this context because it may expose referenced variables outside of their declaration scope
// return X(c += c1);
Diagnostic(ErrorCode.ERR_EscapeVariable, "c").WithArguments("scoped C c").WithLocation(7, 18)
);
}
[Fact]
public void CompoundAssignment_00752_Consumption_RefSafety()
{
var source = $$$"""
ref struct C
{
private ref readonly int _i;
public void operator +=([System.Diagnostics.CodeAnalysis.UnscopedRef] in int right) { _i = ref right; }
public static C operator -(C left, [System.Diagnostics.CodeAnalysis.UnscopedRef] in int right) { left._i = ref right; return left; }
public C M1(C c1)
{
return c1 += 1;
}
public C M2(C c2)
{
return c2 -= 1;
}
public C M3(C c3, in int right)
{
return c3 += right;
}
public C M4(C c4, in int right)
{
return c4 -= right;
}
}
""";
CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(
// (8,16): error CS8350: This combination of arguments to 'C.operator +=(in int)' is disallowed because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return c1 += 1;
Diagnostic(ErrorCode.ERR_CallArgMixing, "c1 += 1").WithArguments("C.operator +=(in int)", "right").WithLocation(8, 16),
// (8,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// return c1 += 1;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(8, 22),
// (12,16): error CS8347: Cannot use a result of 'C.operator -(C, in int)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return c2 -= 1;
Diagnostic(ErrorCode.ERR_EscapeCall, "c2 -= 1").WithArguments("C.operator -(C, in int)", "right").WithLocation(12, 16),
// (12,16): error CS8347: Cannot use a result of 'C.operator -(C, in int)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return c2 -= 1;
Diagnostic(ErrorCode.ERR_EscapeCall, "c2 -= 1").WithArguments("C.operator -(C, in int)", "right").WithLocation(12, 16),
// (12,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// return c2 -= 1;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(12, 22),
// (12,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// return c2 -= 1;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(12, 22),
// (16,16): error CS8350: This combination of arguments to 'C.operator +=(in int)' is disallowed because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return c3 += right;
Diagnostic(ErrorCode.ERR_CallArgMixing, "c3 += right").WithArguments("C.operator +=(in int)", "right").WithLocation(16, 16),
// (16,22): error CS9077: Cannot return a parameter by reference 'right' through a ref parameter; it can only be returned in a return statement
// return c3 += right;
Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "right").WithArguments("right").WithLocation(16, 22),
// (20,16): error CS8347: Cannot use a result of 'C.operator -(C, in int)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return c4 -= right;
Diagnostic(ErrorCode.ERR_EscapeCall, "c4 -= right").WithArguments("C.operator -(C, in int)", "right").WithLocation(20, 16),
// (20,22): error CS9077: Cannot return a parameter by reference 'right' through a ref parameter; it can only be returned in a return statement
// return c4 -= right;
Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "right").WithArguments("right").WithLocation(20, 22)
);
}
[Fact]
public void CompoundAssignment_00753_Consumption_RefSafety()
{
var source = """
ref struct C
{
private ref readonly int _i;
public void operator +=(in int right) { _i = ref right; }
public static C operator -(C left, in int right) { left._i = ref right; return left; }
public C M1(C c1)
{
return c1 += 1;
}
public C M2(C c2)
{
return c2 -= 1;
}
public C M3(C c3, in int right)
{
return c3 += right;
}
public C M4(C c4, in int right)
{
return c4 -= right;
}
}
""";
CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(
// (4,45): error CS9079: Cannot ref-assign 'right' to '_i' because 'right' can only escape the current method through a return statement.
// public void operator +=(in int right) { _i = ref right; }
Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "_i = ref right").WithArguments("_i", "right").WithLocation(4, 45),
// (5,56): error CS9079: Cannot ref-assign 'right' to '_i' because 'right' can only escape the current method through a return statement.
// public static C operator -(C left, in int right) { left._i = ref right; return left; }
Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "left._i = ref right").WithArguments("_i", "right").WithLocation(5, 56),
// (12,16): error CS8347: Cannot use a result of 'C.operator -(C, in int)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return c2 -= 1;
Diagnostic(ErrorCode.ERR_EscapeCall, "c2 -= 1").WithArguments("C.operator -(C, in int)", "right").WithLocation(12, 16),
// (12,16): error CS8347: Cannot use a result of 'C.operator -(C, in int)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return c2 -= 1;
Diagnostic(ErrorCode.ERR_EscapeCall, "c2 -= 1").WithArguments("C.operator -(C, in int)", "right").WithLocation(12, 16),
// (12,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// return c2 -= 1;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(12, 22),
// (12,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// return c2 -= 1;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(12, 22),
// (20,16): error CS8347: Cannot use a result of 'C.operator -(C, in int)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope
// return c4 -= right;
Diagnostic(ErrorCode.ERR_EscapeCall, "c4 -= right").WithArguments("C.operator -(C, in int)", "right").WithLocation(20, 16),
// (20,22): error CS9077: Cannot return a parameter by reference 'right' through a ref parameter; it can only be returned in a return statement
// return c4 -= right;
Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "right").WithArguments("right").WithLocation(20, 22)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00732_Consumption_Used_Struct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool fromMetadata)
{
var source1 = @"
public interface I1
{
public void operator" + op + @"(in long x);
}
public struct C1 : I1
{
public int _F;
public void operator" + op + @"(in long x)
{
System.Console.Write(""[operator]"");
_F = _F + (int)x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static C1[] x = [new C1()];
static void Main()
{
C1 y = Test1(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test2(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test3(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test4(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test5(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
y = Test6(x);
System.Console.Write(y._F);
System.Console.WriteLine(x[0]._F);
}
static C1 Test1(C1[] x)
{
#line 23
return GetA(x)[Get0()]" + op + @" G1();
}
static C1 Test2(C1[] x)
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test3<T>(T[] x) where T : struct, I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test4<T>(T[] x) where T : struct, I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T Test5<T>(T[] x) where T : I1
{
return GetA(x)[Get0()]" + op + @" G1();
}
static T Test6<T>(T[] x) where T : I1
{
checked
{
return GetA(x)[Get0()]" + op + @" G1();
}
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
static int G1()
{
System.Console.Write(""[G1]"");
x[0] = new C1() { _F = -1 };
return 1;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe);
const string expectedOutput = @"
[GetA][Get0][G1][operator]11
[GetA][Get0][G1][operator]22
[GetA][Get0][G1][operator]33
[GetA][Get0][G1][operator]44
[GetA][Get0][G1][operator]55
[GetA][Get0][G1][operator]66
";
var verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics();
var methodName = CompoundAssignmentOperatorName(op, isChecked: false);
verifier.VerifyIL("Program.Test1",
@"
{
// Code size 47 (0x2f)
.maxstack 3
.locals init (C1 V_0,
long V_1)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: dup
IL_0011: ldobj ""C1""
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""int Program.G1()""
IL_001e: conv.i8
IL_001f: stloc.1
IL_0020: ldloca.s V_1
IL_0022: call ""void C1." + methodName + @"(in long)""
IL_0027: ldloc.0
IL_0028: stobj ""C1""
IL_002d: ldloc.0
IL_002e: ret
}
");
verifier.VerifyIL("Program.Test3<T>(T[])",
@"
{
// Code size 51 (0x33)
.maxstack 3
.locals init (int V_0,
T V_1,
long V_2)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: stloc.0
IL_000c: dup
IL_000d: ldloc.0
IL_000e: ldelem ""T""
IL_0013: stloc.1
IL_0014: ldloca.s V_1
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: stloc.2
IL_001d: ldloca.s V_2
IL_001f: constrained. ""T""
IL_0025: callvirt ""void I1." + methodName + @"(in long)""
IL_002a: ldloc.0
IL_002b: ldloc.1
IL_002c: stelem ""T""
IL_0031: ldloc.1
IL_0032: ret
}
");
verifier.VerifyIL("Program.Test2",
@"
{
// Code size 47 (0x2f)
.maxstack 3
.locals init (C1 V_0,
long V_1)
IL_0000: ldarg.0
IL_0001: call ""C1[] Program.GetA<C1>(C1[])""
IL_0006: call ""int Program.Get0()""
IL_000b: ldelema ""C1""
IL_0010: dup
IL_0011: ldobj ""C1""
IL_0016: stloc.0
IL_0017: ldloca.s V_0
IL_0019: call ""int Program.G1()""
IL_001e: conv.i8
IL_001f: stloc.1
IL_0020: ldloca.s V_1
IL_0022: call ""void C1." + methodName + @"(in long)""
IL_0027: ldloc.0
IL_0028: stobj ""C1""
IL_002d: ldloc.0
IL_002e: ret
}
");
verifier.VerifyIL("Program.Test4<T>(T[])",
@"
{
// Code size 51 (0x33)
.maxstack 3
.locals init (int V_0,
T V_1,
long V_2)
IL_0000: ldarg.0
IL_0001: call ""T[] Program.GetA<T>(T[])""
IL_0006: call ""int Program.Get0()""
IL_000b: stloc.0
IL_000c: dup
IL_000d: ldloc.0
IL_000e: ldelem ""T""
IL_0013: stloc.1
IL_0014: ldloca.s V_1
IL_0016: call ""int Program.G1()""
IL_001b: conv.i8
IL_001c: stloc.2
IL_001d: ldloca.s V_2
IL_001f: constrained. ""T""
IL_0025: callvirt ""void I1." + methodName + @"(in long)""
IL_002a: ldloc.0
IL_002b: ldloc.1
IL_002c: stelem ""T""
IL_0031: ldloc.1
IL_0032: ret
}
");
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00800_Consumption_CheckedVersionInRegularContext([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source1 = @"
public class C1
{
public int _F;
public void operator checked " + op + @"(int x)
{
System.Console.Write(""[operator]"");
_F+=x;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1[] x = [new C1()];
Test1(x);
System.Console.WriteLine(x[0]._F);
}
static void Test1(C1[] x)
{
GetA(x)[Get0()] " + op + @" 1;
}
static T[] GetA<T>(T[] x)
{
System.Console.Write(""[GetA]"");
return x;
}
static int Get0()
{
System.Console.Write(""[Get0]"");
return 0;
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (13,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// GetA(x)[Get0()] += 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "GetA(x)[Get0()] " + op + @" 1").WithArguments(op, "C1", "int").WithLocation(13, 9)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00810_Consumption_Shadowing([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
string checkedForm = null;
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
checkedForm = @"
public void operator checked" + op + @"(int x) => throw null;
";
}
var source1 = @"
public class C1
{
public void operator" + op + @"(int x) => throw null;
" + checkedForm + @"
}
public class C2 : C1
{
public new void operator" + op + @"(int x)
{
System.Console.Write(""[operator]"");
}
}
public class Program
{
static void Main()
{
var x = new C2();
x " + op + @" 1;
checked
{
x " + op + @" 1;
}
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "[operator][operator]").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00820_Consumption_Shadowing([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source1_1 = @"
public class C1
{
public void operator" + op + @"(int x) => throw null;
public void operator checked" + op + @"(int x) => throw null;
}
public class C2 : C1
{
public new void operator checked " + op + @"(int x) => throw null;
}
";
var comp1_1 = CreateCompilation([source1_1, CompilerFeatureRequiredAttribute], assemblyName: "C");
var source2 = @"
public class Test
{
public static void Main()
{
var x = new C2();
x " + op + @" 1;
checked
{
x " + op + @" 1;
}
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1_1.ToMetadataReference()]);
comp2.VerifyDiagnostics();
var source1_2 = @"
public class C1
{
public void operator" + op + @"(int x)
{
System.Console.Write(""[operator]"");
}
public void operator checked" + op + @"(int x) => throw null;
}
public class C2 : C1
{
public new void operator checked " + op + @"(int x)
{
System.Console.Write(""[checked operator]"");
}
public new void operator " + op + @"(int x) => throw null;
}
";
var source3 = @"
public class Program
{
static void Main()
{
Test.Main();
}
}
";
var comp1_2 = CreateCompilation([source1_2, CompilerFeatureRequiredAttribute], assemblyName: "C");
var comp3 = CreateCompilation([source3, CompilerFeatureRequiredAttribute], references: [comp1_2.EmitToImageReference(), comp2.EmitToImageReference()], options: TestOptions.DebugExe);
CompileAndVerify(comp3, expectedOutput: "[operator][checked operator]").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00830_Consumption_Shadowing([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source1 = @"
public abstract class C1
{
public abstract void operator" + op + @"(int x);
public void operator checked" + op + @"(int x) => throw null;
}
public class C2 : C1
{
public new void operator checked " + op + @"(int x)
{
System.Console.Write(""[checked operator]"");
}
public override void operator " + op + @"(int x)
{
System.Console.Write(""[operator]"");
}
}
public class Program
{
static void Main()
{
var x = new C2();
x " + op + @" 1;
checked
{
x " + op + @" 1;
}
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "[operator][checked operator]").VerifyDiagnostics();
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00840_Consumption_Overriding([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source1 = @"
public abstract class C1
{
public abstract void operator" + op + @"(int x);
public abstract void operator checked" + op + @"(int x);
}
public abstract class C2 : C1
{
public override void operator checked " + op + @"(int x)
{
System.Console.Write(""[checked operator]"");
}
}
public class C3 : C2
{
public override void operator " + op + @"(int x)
{
System.Console.Write(""[operator]"");
}
}
public class Program
{
static void Main()
{
var x = new C3();
x " + op + @" 1;
checked
{
x " + op + @" 1;
}
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "[operator][checked operator]").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_00850_Consumption_Ambiguity()
{
var source1 = @"
public interface I1
{
public void operator +=(int x);
}
public interface I2<T> where T : I2<T>
{
public void operator +=(int x);
public abstract static T operator +(T x, int y);
public abstract static T operator -(T x, int y);
}
public class Program
{
static void Test5<T>(T x) where T : I1, I2<T>
{
x += 1;
x -= 1;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
comp1.VerifyDiagnostics(
// (18,11): error CS0121: The call is ambiguous between the following methods or properties: 'I1.operator +=(int)' and 'I2<T>.operator +=(int)'
// x += 1;
Diagnostic(ErrorCode.ERR_AmbigCall, "+=").WithArguments("I1.operator +=(int)", "I2<T>.operator +=(int)").WithLocation(18, 11)
);
var tree = comp1.SyntaxTrees.First();
var model = comp1.GetSemanticModel(tree);
var opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().First();
var symbolInfo = model.GetSymbolInfo(opNode);
Assert.Null(symbolInfo.Symbol);
Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason);
Assert.Equal(2, symbolInfo.CandidateSymbols.Length);
Assert.Equal("void I1.op_AdditionAssignment(System.Int32 x)", symbolInfo.CandidateSymbols[0].ToTestDisplayString());
Assert.Equal("void I2<T>.op_AdditionAssignment(System.Int32 x)", symbolInfo.CandidateSymbols[1].ToTestDisplayString());
var group = model.GetMemberGroup(opNode);
Assert.Empty(group);
opNode = tree.GetRoot().DescendantNodes().OfType<Syntax.AssignmentExpressionSyntax>().Last();
symbolInfo = model.GetSymbolInfo(opNode);
Assert.Equal("T I2<T>.op_Subtraction(T x, System.Int32 y)", symbolInfo.Symbol.ToTestDisplayString());
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
Assert.Empty(symbolInfo.CandidateSymbols);
group = model.GetMemberGroup(opNode);
Assert.Empty(group);
}
[Fact]
public void CompoundAssignment_00860_Consumption_UseStaticOperatorsInOldVersion()
{
var source1 = @"
public class C1
{
public int _F;
public void operator +=(int x)
{
System.Console.Write(""[instance operator]"");
_F+=x;
}
public void operator checked +=(int x)
{
System.Console.Write(""[instance operator checked]"");
checked
{
_F+=x;
}
}
public static C1 operator +(C1 x, int y)
{
System.Console.Write(""[static operator]"");
return new C1() { _F = x._F + y };
}
public static C1 operator checked +(C1 x, int y)
{
System.Console.Write(""[static operator checked]"");
checked
{
return new C1() { _F = x._F + y };
}
}
}
";
var comp1Ref = CreateCompilation([source1, CompilerFeatureRequiredAttribute]).EmitToImageReference();
var source2 = @"
public class Program
{
static C1 P = new C1();
static void Main()
{
C1 x;
P += 1;
System.Console.WriteLine(P._F);
x = P += 1;
System.Console.WriteLine(P._F);
checked
{
P += 1;
System.Console.WriteLine(P._F);
x = P += 1;
System.Console.WriteLine(P._F);
}
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1Ref], options: TestOptions.DebugExe);
CompileAndVerify(comp2, expectedOutput: @"
[instance operator]1
[instance operator]2
[instance operator checked]3
[instance operator checked]4
").VerifyDiagnostics();
comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1Ref], options: TestOptions.DebugExe, parseOptions: TestOptions.Regular13);
CompileAndVerify(comp2, expectedOutput: @"
[static operator]1
[static operator]2
[static operator checked]3
[static operator checked]4
").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_00870_Consumption_Obsolete()
{
var source1 = @"
public class C1
{
[System.Obsolete(""Test"")]
public void operator +=(int x) {}
}
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1).VerifyDiagnostics(
// (13,9): warning CS0618: 'C1.operator +=(int)' is obsolete: 'Test'
// x += 1;
Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "x += 1").WithArguments("C1.operator +=(int)", "Test").WithLocation(13, 9)
);
}
[Fact]
public void CompoundAssignment_00880_Consumption_UnmanagedCallersOnly()
{
var source1 = @"
public class C1
{
[System.Runtime.InteropServices.UnmanagedCallersOnly]
public void operator +=(int x) {}
}
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe);
comp1.VerifyDiagnostics(
// (4,6): error CS8896: 'UnmanagedCallersOnly' can only be applied to ordinary static non-abstract, non-virtual methods or static local functions.
// [System.Runtime.InteropServices.UnmanagedCallersOnly]
Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyRequiresStatic, "System.Runtime.InteropServices.UnmanagedCallersOnly").WithLocation(4, 6),
// (13,9): error CS8901: 'C1.operator +=(int)' is attributed with 'UnmanagedCallersOnly' and cannot be called directly. Obtain a function pointer to this method.
// x += 1;
Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeCalledDirectly, "x += 1").WithArguments("C1.operator +=(int)").WithLocation(13, 9)
);
}
[Fact]
public void CompoundAssignment_00890_Consumption_NullableAnalysis()
{
var source1 = @"
public class C1
{
public void operator +=(int x) {}
}
#nullable enable
public class Program
{
static void Main()
{
C1? x = null;
try
{
x += 1;
System.Console.Write(""unreachable"");
x.ToString();
}
catch (System.NullReferenceException)
{
System.Console.Write(""in catch"");
}
C1? y = new C1();
y += 1;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "in catch").VerifyDiagnostics(
// (17,13): warning CS8602: Dereference of a possibly null reference.
// x += 1;
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(17, 13)
);
var source2 = @"
public class C1
{
public void operator +=(int x) {}
}
#nullable enable
public class Program
{
static void Main()
{
C1? x = null;
if (false)
{
x += 1;
System.Console.Write(""unreachable"");
x.ToString();
}
System.Console.Write(""Done"");
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp2, expectedOutput: "Done").VerifyDiagnostics(
// (17,13): warning CS0162: Unreachable code detected
// x += 1;
Diagnostic(ErrorCode.WRN_UnreachableCode, "x").WithLocation(17, 13)
);
}
[Fact]
public void CompoundAssignment_00891_Consumption_NullableAnalysis()
{
var source1 = @"
public class C1<T>
{
public void operator +=(T x) {}
}
#nullable enable
public class Program
{
static C1<T> GetC1<T>(T x) => new C1<T>();
static void Main()
{
string? x = null;
var c1 = GetC1(new object());
c1 += x;
x.ToString();
var c2 = GetC1((object?)null);
c2 += null;
c2 += (string?)null;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
comp1.VerifyDiagnostics(
// (18,15): warning CS8604: Possible null reference argument for parameter 'x' in 'void C1<object>.operator +=(object x)'.
// c1 += x;
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("x", "void C1<object>.operator +=(object x)").WithLocation(18, 15)
);
}
[Fact]
public void CompoundAssignment_00900_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
public void operator +=(int a, int x = 0) {}
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (4,26): error CS1020: Overloadable binary operator expected
// public void operator +=(int a, int x = 0) {}
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, "+=").WithLocation(4, 26),
// (4,40): warning CS1066: The default value specified for parameter 'x' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
// public void operator +=(int a, int x = 0) {}
Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "x").WithArguments("x").WithLocation(4, 40)
);
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// x += 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += 1").WithArguments("+=", "C1", "int").WithLocation(7, 9)
);
}
[Fact]
public void CompoundAssignment_00901_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
public int operator +=(int a) { return 0; }
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (4,25): error CS9310: The return type for this operator must be void
// public int operator +=(int a) { return 0; }
Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, "+=").WithLocation(4, 25)
);
var comp2 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// x += 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += 1").WithArguments("+=", "C1", "int").WithLocation(7, 9)
);
}
[Fact]
public void CompoundAssignment_00902_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
public void operator +=(__arglist) {}
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (4,29): error CS1669: __arglist is not valid in this context
// public void operator +=(__arglist) {}
Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(4, 29)
);
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// x += 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += 1").WithArguments("+=", "C1", "int").WithLocation(7, 9)
);
}
[Fact]
public void CompoundAssignment_00903_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
public void operator +=(int x, __arglist) {}
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (4,26): error CS1020: Overloadable binary operator expected
// public void operator +=(int x, __arglist) {}
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, "+=").WithLocation(4, 26),
// (4,36): error CS1669: __arglist is not valid in this context
// public void operator +=(int x, __arglist) {}
Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(4, 36)
);
var comp2 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyEmitDiagnostics();
var method = comp2.GetMember<MethodSymbol>("C1." + WellKnownMemberNames.AdditionAssignmentOperatorName);
Assert.False(method.IsVararg);
AssertEx.Equal("void C1.op_AdditionAssignment(System.Int32 x)", method.ToTestDisplayString());
}
[Fact]
public void CompoundAssignment_00904_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
public void operator +=(ref int x) {}
public void operator -=(ref readonly int x) {}
public void operator *=(out int x) { x = 0; }
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
x -= 1;
x *= 1;
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (4,26): error CS0631: ref and out are not valid in this context
// public void operator +=(ref int x) {}
Diagnostic(ErrorCode.ERR_IllegalRefParam, "+=").WithLocation(4, 26),
// (5,26): error CS0631: ref and out are not valid in this context
// public void operator -=(ref readonly int x) {}
Diagnostic(ErrorCode.ERR_IllegalRefParam, "-=").WithLocation(5, 26),
// (6,26): error CS0631: ref and out are not valid in this context
// public void operator *=(out int x) { x = 0; }
Diagnostic(ErrorCode.ERR_IllegalRefParam, "*=").WithLocation(6, 26)
);
var comp2 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// x += 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += 1").WithArguments("+=", "C1", "int").WithLocation(7, 9),
// (8,9): error CS0019: Operator '-=' cannot be applied to operands of type 'C1' and 'int'
// x -= 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x -= 1").WithArguments("-=", "C1", "int").WithLocation(8, 9),
// (9,9): error CS0019: Operator '*=' cannot be applied to operands of type 'C1' and 'int'
// x *= 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x *= 1").WithArguments("*=", "C1", "int").WithLocation(9, 9)
);
}
[Fact]
public void CompoundAssignment_00910_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
public void operator +=(params int[] x) {}
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (4,29): error CS1670: params is not valid in this context
// public void operator +=(params int[] x) {}
Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(4, 29)
);
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// x += 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += 1").WithArguments("+=", "C1", "int").WithLocation(7, 9)
);
}
[Fact]
public void CompoundAssignment_00911_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
[System.Runtime.CompilerServices.SpecialName]
public void " + WellKnownMemberNames.AdditionAssignmentOperatorName + @"(params int[] x) {}
[System.Runtime.CompilerServices.SpecialName]
public void " + WellKnownMemberNames.AdditionAssignmentOperatorName + @"(string x) {}
[System.Runtime.CompilerServices.SpecialName]
public void " + WellKnownMemberNames.AdditionAssignmentOperatorName + @"(string[] x) {}
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
x += [2];
x += ""3"";
x += [""4""];
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyEmitDiagnostics();
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// x += 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += 1").WithArguments("+=", "C1", "int").WithLocation(7, 9),
// (8,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'collection expression'
// x += [2];
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += [2]").WithArguments("+=", "C1", "collection expression").WithLocation(8, 9)
);
}
[Fact]
public void CompoundAssignment_00912_Consumption_BadOperator()
{
var source1 = @"
public class C1
{
public void operator +=(int a, params int[] x) {}
}
";
var source2 = @"
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (4,26): error CS1020: Overloadable binary operator expected
// public void operator +=(int a, params int[] x) {}
Diagnostic(ErrorCode.ERR_OvlBinaryOperatorExpected, "+=").WithLocation(4, 26),
// (4,36): error CS1670: params is not valid in this context
// public void operator +=(int a, params int[] x) {}
Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(4, 36)
);
var comp2 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// x += 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += 1").WithArguments("+=", "C1", "int").WithLocation(7, 9)
);
}
[Fact]
public void CompoundAssignment_00920_ConflictWithRegular()
{
var source1 = @"
public class C1
{
public void operator +=(int x) {}
#line 1000
public void op_AdditionAssignment(int x) {}
}
public class C2
{
public void op_AdditionAssignment(int x) {}
#line 2000
public void operator +=(int x) {}
}
public class C3
{
public void operator +=(int x) {}
public void op_AdditionAssignment(long x) {}
}
public class C4
{
public void op_AdditionAssignment(int x) {}
public void operator +=(long x) {}
}
";
CSharpCompilation comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
comp1.VerifyDiagnostics(
// (1000,17): error CS0111: Type 'C1' already defines a member called 'op_AdditionAssignment' with the same parameter types
// public void op_AdditionAssignment(int x) {}
Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "op_AdditionAssignment").WithArguments("op_AdditionAssignment", "C1").WithLocation(1000, 17),
// (2000,26): error CS0111: Type 'C2' already defines a member called 'op_AdditionAssignment' with the same parameter types
// public void operator +=(int x) {}
Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "+=").WithArguments("op_AdditionAssignment", "C2").WithLocation(2000, 26)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00930_Consumption_RegularVsOperator(bool fromMetadata)
{
var source1 = @"
public class C1
{
public void op_AdditionAssignment(int x) {}
}
public class C2
{
public void operator+=(int x) {}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class Program
{
static void Main()
{
C1 x = new C1();
x += 1;
C2 y = new C2();
y.op_AdditionAssignment(1);
}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.DebugExe);
comp2.VerifyDiagnostics(
// (7,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int'
// x += 1;
Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += 1").WithArguments("+=", "C1", "int").WithLocation(7, 9),
// (9,11): error CS0571: 'C2.operator +=(int)': cannot explicitly call operator or accessor
// y.op_AdditionAssignment(1);
Diagnostic(ErrorCode.ERR_CantCallSpecialMethod, "op_AdditionAssignment").WithArguments("C2.operator +=(int)").WithLocation(9, 11)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00940_Override_RegularVsOperatorMismatch(bool fromMetadata)
{
var source1 = @"
abstract public class C1
{
public abstract void op_AdditionAssignment(int x);
}
abstract public class C3
{
public abstract void operator +=(int x);
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class C2 : C1
{
public override void operator +=(int x) {}
}
public class C4 : C3
{
public override void op_AdditionAssignment(int x) {}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()]);
comp2.VerifyDiagnostics(
// (4,35): error CS9312: 'C2.operator +=(int)': cannot override inherited member 'C1.op_AdditionAssignment(int)' because one of them is not an operator.
// public override void operator +=(int x) {}
Diagnostic(ErrorCode.ERR_OperatorMismatchOnOverride, "+=").WithArguments("C2.operator +=(int)", "C1.op_AdditionAssignment(int)").WithLocation(4, 35),
// (9,26): error CS9312: 'C4.op_AdditionAssignment(int)': cannot override inherited member 'C3.operator +=(int)' because one of them is not an operator.
// public override void op_AdditionAssignment(int x) {}
Diagnostic(ErrorCode.ERR_OperatorMismatchOnOverride, "op_AdditionAssignment").WithArguments("C4.op_AdditionAssignment(int)", "C3.operator +=(int)").WithLocation(9, 26)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00950_Implement_RegularVsOperatorMismatch(bool fromMetadata)
{
var source1 = @"
public interface I1
{
void op_AdditionAssignment(int x);
}
public interface I2
{
void operator +=(int x);
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class C1 : I1
{
public void operator +=(int x) {}
}
public class C2 : I2
{
public void op_AdditionAssignment(int x) {}
}
public class C3 : I1
{
void I1.operator +=(int x) {}
}
public class C4 : I2
{
void I2.op_AdditionAssignment(int x) {}
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()]);
comp2.VerifyDiagnostics(
// (2,19): error CS9311: 'C1' does not implement interface member 'I1.op_AdditionAssignment(int)'. 'C1.operator +=(int)' cannot implement 'I1.op_AdditionAssignment(int)' because one of them is not an operator.
// public class C1 : I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C1", "I1.op_AdditionAssignment(int)", "C1.operator +=(int)").WithLocation(2, 19),
// (7,19): error CS9311: 'C2' does not implement interface member 'I2.operator +=(int)'. 'C2.op_AdditionAssignment(int)' cannot implement 'I2.operator +=(int)' because one of them is not an operator.
// public class C2 : I2
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I2").WithArguments("C2", "I2.operator +=(int)", "C2.op_AdditionAssignment(int)").WithLocation(7, 19),
// (12,19): error CS0535: 'C3' does not implement interface member 'I1.op_AdditionAssignment(int)'
// public class C3 : I1
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I1").WithArguments("C3", "I1.op_AdditionAssignment(int)").WithLocation(12, 19),
// (14,22): error CS0539: 'C3.operator +=(int)' in explicit interface declaration is not found among members of the interface that can be implemented
// void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "+=").WithArguments("C3.operator +=(int)").WithLocation(14, 22),
// (17,19): error CS0535: 'C4' does not implement interface member 'I2.operator +=(int)'
// public class C4 : I2
Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "I2").WithArguments("C4", "I2.operator +=(int)").WithLocation(17, 19),
// (19,13): error CS0539: 'C4.op_AdditionAssignment(int)' in explicit interface declaration is not found among members of the interface that can be implemented
// void I2.op_AdditionAssignment(int x) {}
Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "op_AdditionAssignment").WithArguments("C4.op_AdditionAssignment(int)").WithLocation(19, 13)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_00960_Implement_RegularVsOperatorMismatch(bool fromMetadata)
{
var source1 = @"
public interface I1
{
void op_AdditionAssignment(int x);
}
public interface I2
{
void operator +=(int x);
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute]);
var source2 = @"
public class C11
{
public void operator +=(int x) {}
}
public class C12 : C11, I1
{
}
public class C21
{
public void op_AdditionAssignment(int x) {}
}
public class C22 : C21, I2
{
}
";
var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()]);
comp2.VerifyDiagnostics(
// (7,25): error CS9311: 'C12' does not implement interface member 'I1.op_AdditionAssignment(int)'. 'C11.operator +=(int)' cannot implement 'I1.op_AdditionAssignment(int)' because one of them is not an operator.
// public class C12 : C11, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C12", "I1.op_AdditionAssignment(int)", "C11.operator +=(int)").WithLocation(7, 25),
// (16,25): error CS9311: 'C22' does not implement interface member 'I2.operator +=(int)'. 'C21.op_AdditionAssignment(int)' cannot implement 'I2.operator +=(int)' because one of them is not an operator.
// public class C22 : C21, I2
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I2").WithArguments("C22", "I2.operator +=(int)", "C21.op_AdditionAssignment(int)").WithLocation(16, 25)
);
}
[Fact]
public void CompoundAssignment_00970_Implement_RegularVsOperatorMismatch()
{
var source = @"
public interface I1
{
public void operator +=(int x)
{
System.Console.Write(""[I1.operator]"");
}
}
public class C1 : I1
{
public virtual void op_AdditionAssignment(int x)
{
System.Console.Write(""[C1.op_AdditionAssignment]"");
}
}
public class C2
{
public virtual void op_AdditionAssignment(int x)
{
System.Console.Write(""[C2.op_AdditionAssignment]"");
}
}
public class C3 : C2, I1
{
}
public class Program
{
static void Main()
{
I1 x = new C1();
x += 1;
x = new C3();
x += 1;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (10,19): error CS9311: 'C1' does not implement interface member 'I1.operator +=(int)'. 'C1.op_AdditionAssignment(int)' cannot implement 'I1.operator +=(int)' because one of them is not an operator.
// public class C1 : I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C1", "I1.operator +=(int)", "C1.op_AdditionAssignment(int)").WithLocation(10, 19),
// (26,23): error CS9311: 'C3' does not implement interface member 'I1.operator +=(int)'. 'C2.op_AdditionAssignment(int)' cannot implement 'I1.operator +=(int)' because one of them is not an operator.
// public class C3 : C2, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C3", "I1.operator +=(int)", "C2.op_AdditionAssignment(int)").WithLocation(26, 23)
);
}
[Fact]
public void CompoundAssignment_00980_Implement_RegularVsOperatorMismatch()
{
var source = @"
public interface I1
{
public void op_AdditionAssignment(int x)
{
System.Console.Write(""[I1.operator]"");
}
}
public class C1 : I1
{
public virtual void operator +=(int x)
{
System.Console.Write(""[C1.operator]"");
}
}
public class C2
{
public virtual void operator +=(int x)
{
System.Console.Write(""[C2.operator]"");
}
}
public class C3 : C2, I1
{
}
public class Program
{
static void Main()
{
I1 x = new C1();
x.op_AdditionAssignment(1);
x = new C3();
x.op_AdditionAssignment(1);
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (10,19): error CS9311: 'C1' does not implement interface member 'I1.op_AdditionAssignment(int)'. 'C1.operator +=(int)' cannot implement 'I1.op_AdditionAssignment(int)' because one of them is not an operator.
// public class C1 : I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C1", "I1.op_AdditionAssignment(int)", "C1.operator +=(int)").WithLocation(10, 19),
// (26,23): error CS9311: 'C3' does not implement interface member 'I1.op_AdditionAssignment(int)'. 'C2.operator +=(int)' cannot implement 'I1.op_AdditionAssignment(int)' because one of them is not an operator.
// public class C3 : C2, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C3", "I1.op_AdditionAssignment(int)", "C2.operator +=(int)").WithLocation(26, 23)
);
}
[Fact]
public void CompoundAssignment_00990_Implement_RegularVsOperatorMismatch()
{
var source = @"
public interface I1
{
public void operator +=(int x);
}
public class C2 : I1
{
void I1.operator +=(int x)
{
System.Console.Write(""[C2.operator]"");
}
}
public class C3 : C2, I1
{
public virtual void op_AdditionAssignment(int x)
{
System.Console.Write(""[C3.op_IncrementAssignment]"");
}
}
public class Program
{
static void Main()
{
I1 x = new C3();
x += 1;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (15,23): error CS9311: 'C3' does not implement interface member 'I1.operator +=(int)'. 'C3.op_AdditionAssignment(int)' cannot implement 'I1.operator +=(int)' because one of them is not an operator.
// public class C3 : C2, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C3", "I1.operator +=(int)", "C3.op_AdditionAssignment(int)").WithLocation(15, 23)
);
}
[Fact]
public void CompoundAssignment_01000_Implement_RegularVsOperatorMismatch()
{
var source = @"
public interface I1
{
public void op_AdditionAssignment(int x);
}
public class C2 : I1
{
void I1.op_AdditionAssignment(int x)
{
System.Console.Write(""[C2.op_IncrementAssignment]"");
}
}
public class C3 : C2, I1
{
public virtual void operator +=(int x)
{
System.Console.Write(""[C3.operator]"");
}
}
public class Program
{
static void Main()
{
I1 x = new C3();
x.op_AdditionAssignment(1);
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (15,23): error CS9311: 'C3' does not implement interface member 'I1.op_AdditionAssignment(int)'. 'C3.operator +=(int)' cannot implement 'I1.op_AdditionAssignment(int)' because one of them is not an operator.
// public class C3 : C2, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C3", "I1.op_AdditionAssignment(int)", "C3.operator +=(int)").WithLocation(15, 23)
);
}
[Fact]
public void CompoundAssignment_01010_Implement_RegularVsOperatorMismatch()
{
/*
public interface I1
{
public void operator+=(int x);
}
public class C1 : I1
{
public virtual void op_AdditionAssignment(int x)
{
System.Console.Write(1);
}
}
*/
var ilSource = @"
.class interface public auto ansi abstract beforefieldinit I1
{
.method public hidebysig newslot abstract virtual specialname
instance void op_AdditionAssignment (int32 x) cil managed
{
}
}
.class public auto ansi beforefieldinit C1
extends [mscorlib]System.Object
implements I1
{
.method public hidebysig newslot virtual
instance void op_AdditionAssignment (int32 x) cil managed
{
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: call void [mscorlib]System.Console::Write(int32)
IL_0006: ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
}
}
";
var source1 =
@"
public class C2 : C1, I1
{
}
";
var compilation1 = CreateCompilationWithIL(source1, ilSource);
compilation1.VerifyDiagnostics(
// (2,23): error CS9311: 'C2' does not implement interface member 'I1.operator +=(int)'. 'C1.op_AdditionAssignment(int)' cannot implement 'I1.operator +=(int)' because one of them is not an operator.
// public class C2 : C1, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C2", "I1.operator +=(int)", "C1.op_AdditionAssignment(int)").WithLocation(2, 23)
);
var source2 =
@"
class Program
{
static void Main()
{
var c1 = new C1();
c1.op_AdditionAssignment(1);
I1 x = c1;
x += 1;
}
}
";
var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe);
CompileAndVerify(compilation2, expectedOutput: "11", verify: Verification.Skipped).VerifyDiagnostics();
var i1M1 = compilation1.GetTypeByMetadataName("I1").GetMembers().Single();
var c1 = compilation1.GetTypeByMetadataName("C1");
AssertEx.Equal("C1.op_AdditionAssignment(int)", c1.FindImplementationForInterfaceMember(i1M1).ToDisplayString());
}
[Fact]
public void CompoundAssignment_01020_Implement_RegularVsOperatorMismatch()
{
/*
public interface I1
{
public void op_AdditionAssignment(int x);
}
public class C1 : I1
{
public virtual void operator+=(int x)
{
System.Console.Write(1);
}
}
*/
var ilSource = @"
.class interface public auto ansi abstract beforefieldinit I1
{
.method public hidebysig newslot abstract virtual
instance void op_AdditionAssignment (int32 x) cil managed
{
}
}
.class public auto ansi beforefieldinit C1
extends [mscorlib]System.Object
implements I1
{
.method public hidebysig newslot virtual specialname
instance void op_AdditionAssignment (int32 x) cil managed
{
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: call void [mscorlib]System.Console::Write(int32)
IL_0006: ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
}
}
";
var source1 =
@"
public class C2 : C1, I1
{
}
";
var compilation1 = CreateCompilationWithIL(source1, ilSource);
compilation1.VerifyDiagnostics(
// (2,23): error CS9311: 'C2' does not implement interface member 'I1.op_AdditionAssignment(int)'. 'C1.operator +=(int)' cannot implement 'I1.op_AdditionAssignment(int)' because one of them is not an operator.
// public class C2 : C1, I1
Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch, "I1").WithArguments("C2", "I1.op_AdditionAssignment(int)", "C1.operator +=(int)").WithLocation(2, 23)
);
var source2 =
@"
class Program
{
static void Main()
{
var c1 = new C1();
c1 += 1;
I1 x = c1;
x.op_AdditionAssignment(1);
}
}
";
var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe);
CompileAndVerify(compilation2, expectedOutput: "11", verify: Verification.Skipped).VerifyDiagnostics();
var i1M1 = compilation1.GetTypeByMetadataName("I1").GetMembers().Single();
var c1 = compilation1.GetTypeByMetadataName("C1");
AssertEx.Equal("C1.operator +=(int)", c1.FindImplementationForInterfaceMember(i1M1).ToDisplayString());
}
[Fact]
public void CompoundAssignment_01030_Consumption_Implementation()
{
var source = @"
public interface I1
{
public void operator +=(int x);
}
public class C1 : I1
{
public void operator +=(int x)
{
System.Console.Write(""[C1.operator]"");
}
}
public class C2 : I1
{
void I1.operator +=(int x)
{
System.Console.Write(""[C2.operator]"");
}
}
public class Program
{
static void Main()
{
I1 x = new C1();
x += 1;
x = new C2();
x += 1;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "[C1.operator][C2.operator]").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_01040_Consumption_Overriding()
{
var source = @"
public abstract class C1
{
public abstract void operator +=(int x);
}
public class C2 : C1
{
public override void operator +=(int x)
{
System.Console.Write(""[C2.operator]"");
}
}
public class Program
{
static void Main()
{
C1 x = new C2();
x += 1;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
CompileAndVerify(comp, expectedOutput: "[C2.operator]").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_01050_Shadow_RegularVsOperatorMismatch()
{
var source = @"
public class C1
{
public void operator +=(int x){}
}
public class C2 : C1
{
public void op_AdditionAssignment(int x){}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void CompoundAssignment_01060_Shadow_RegularVsOperatorMismatch()
{
var source = @"
public class C2
{
public void op_AdditionAssignment(int x){}
}
public class C1 : C2
{
public void operator +=(int x){}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyEmitDiagnostics();
}
private static string ToCRefOp(string op)
{
return op.Replace("&", "&").Replace("<", "<");
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01070_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + ToCRefOp(op) + @"()""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator " + op + @"(long x) {}
}
/// <summary>
/// See <see cref=""C1.operator " + ToCRefOp(op) + @"()""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator +=()' that could not be resolved
// /// See <see cref="operator +=()"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator " + ToCRefOp(op) + @"()").WithArguments("operator " + ToCRefOp(op) + @"()").WithLocation(3, 20),
// (12,20): warning CS1574: XML comment has cref attribute 'operator +=()' that could not be resolved
// /// See <see cref="C1.operator +=()"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator " + ToCRefOp(op) + @"()").WithArguments("operator " + ToCRefOp(op) + @"()").WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01080_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + ToCRefOp(op) + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
}
/// <summary>
/// See <see cref=""C1.operator " + ToCRefOp(op) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator " + op + @"(int)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01100_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + ToCRefOp(op) + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator " + op + @"(long x) {}
}
/// <summary>
/// See <see cref=""C1.operator " + ToCRefOp(op) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS0419: Ambiguous reference in cref attribute: 'operator +='. Assuming 'C1.operator +=(int)', but could have also matched other overloads including 'C1.operator +=(long)'.
// /// See <see cref="operator +="/>.
Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "operator " + ToCRefOp(op)).WithArguments("operator " + ToCRefOp(op), "C1.operator " + op + @"(int)", "C1.operator " + op + @"(long)").WithLocation(3, 20),
// (12,20): warning CS0419: Ambiguous reference in cref attribute: 'C1.operator +='. Assuming 'C1.operator +=(int)', but could have also matched other overloads including 'C1.operator +=(long)'.
// /// See <see cref="C1.operator +="/>.
Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "C1.operator " + ToCRefOp(op)).WithArguments("C1.operator " + ToCRefOp(op), "C1.operator " + op + @"(int)", "C1.operator " + op + @"(long)").WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbols = GetReferencedSymbols(crefSyntax, compilation, out var ambiguityWinner, expected[count]);
AssertEx.Equal("C1.operator " + op + @"(int)", ambiguityWinner.ToDisplayString());
Assert.Equal(2, actualSymbols.Length);
AssertEx.Equal("C1.operator " + op + @"(int)", actualSymbols[0].ToDisplayString());
AssertEx.Equal("C1.operator " + op + @"(long)", actualSymbols[1].ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01120_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + ToCRefOp(op) + @"(int)""/>.
/// </summary>
class C1
{
public void operator " + op + @"(long x) {}
public void operator " + op + @"(int x) {}
}
/// <summary>
/// See <see cref=""C1.operator " + ToCRefOp(op) + @"(int)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator " + op + @"(int)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01130_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + ToCRefOp(op) + @"(string)""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator " + op + @"(long x) {}
}
/// <summary>
/// See <see cref=""C1.operator " + ToCRefOp(op) + @"(string)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator +=(string)' that could not be resolved
// /// See <see cref="operator +=(string)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator " + ToCRefOp(op) + @"(string)").WithArguments("operator " + ToCRefOp(op) + @"(string)").WithLocation(3, 20),
// (12,20): warning CS1574: XML comment has cref attribute 'operator +=(string)' that could not be resolved
// /// See <see cref="C1.operator +=(string)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator " + ToCRefOp(op) + @"(string)").WithArguments("operator " + ToCRefOp(op) + @"(string)").WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01131_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + ToCRefOp(op) + @"(int, string)""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator " + op + @"(long x) {}
}
/// <summary>
/// See <see cref=""C1.operator " + ToCRefOp(op) + @"(int, string)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator +=(int, string)' that could not be resolved
// /// See <see cref="operator +=(int, string)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator " + ToCRefOp(op) + @"(int, string)").WithArguments("operator " + ToCRefOp(op) + @"(int, string)").WithLocation(3, 20),
// (12,20): warning CS1574: XML comment has cref attribute 'operator +=(int, string)' that could not be resolved
// /// See <see cref="C1.operator +=(int, string)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator " + ToCRefOp(op) + @"(int, string)").WithArguments("operator " + ToCRefOp(op) + @"(int, string)").WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void CompoundAssignment_01140_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator " + ToCRefOp(op) + @"""/>.
/// </summary>
class C1
{
public static void " + CompoundAssignmentOperatorName(op, isChecked: false) + @"(int x, long y) {}
}
/// <summary>
/// See <see cref=""C1.operator " + ToCRefOp(op) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1." + CompoundAssignmentOperatorName(op, isChecked: false) + @"(int, long)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void CompoundAssignment_01160_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""" + CompoundAssignmentOperatorName(op, isChecked: false) + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
}
/// <summary>
/// See <see cref=""C1." + CompoundAssignmentOperatorName(op, isChecked: false) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator " + op + @"(int)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01170_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + ToCRefOp(op) + @"()""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator checked " + op + @"(int x) {}
public void operator " + op + @"(long x) {}
public void operator checked " + op + @"(long x) {}
}
#line 11
/// <summary>
/// See <see cref=""C1.operator checked " + ToCRefOp(op) + @"()""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator checked +=()' that could not be resolved
// /// See <see cref="operator checked +=()"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator checked " + ToCRefOp(op) + @"()").WithArguments("operator checked " + ToCRefOp(op) + @"()").WithLocation(3, 20),
// (12,20): warning CS1574: XML comment has cref attribute 'operator checked +=()' that could not be resolved
// /// See <see cref="C1.operator checked +=()"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + ToCRefOp(op) + @"()").WithArguments("operator checked " + ToCRefOp(op) + @"()").WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01180_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + ToCRefOp(op) + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator checked " + op + @"(int x) {}
}
/// <summary>
/// See <see cref=""C1.operator checked " + ToCRefOp(op) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator checked " + op + @"(int)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01181_CRef_Checked([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + ToCRefOp(op) + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
}
#line 11
/// <summary>
/// See <see cref=""C1.operator checked " + ToCRefOp(op) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator checked %=' that could not be resolved
// /// See <see cref="operator checked %="/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator checked " + ToCRefOp(op)).WithArguments("operator checked " + ToCRefOp(op)).WithLocation(3, 20),
// (12,20): warning CS1574: XML comment has cref attribute 'operator checked %=' that could not be resolved
// /// See <see cref="C1.operator checked %="/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + ToCRefOp(op)).WithArguments("operator checked " + ToCRefOp(op)).WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01200_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + ToCRefOp(op) + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator checked " + op + @"(int x) {}
public void operator " + op + @"(long x) {}
public void operator checked " + op + @"(long x) {}
}
#line 11
/// <summary>
/// See <see cref=""C1.operator checked " + ToCRefOp(op) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS0419: Ambiguous reference in cref attribute: 'operator checked +='. Assuming 'C1.operator checked +=(int)', but could have also matched other overloads including 'C1.operator checked +=(long)'.
// /// See <see cref="operator checked +="/>.
Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "operator checked " + ToCRefOp(op)).WithArguments("operator checked " + ToCRefOp(op), "C1.operator checked " + op + @"(int)", "C1.operator checked " + op + @"(long)").WithLocation(3, 20),
// (12,20): warning CS0419: Ambiguous reference in cref attribute: 'C1.operator checked +='. Assuming 'C1.operator checked +=(int)', but could have also matched other overloads including 'C1.operator checked +=(long)'.
// /// See <see cref="C1.operator +="/>.
Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "C1.operator checked " + ToCRefOp(op)).WithArguments("C1.operator checked " + ToCRefOp(op), "C1.operator checked " + op + @"(int)", "C1.operator checked " + op + @"(long)").WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbols = GetReferencedSymbols(crefSyntax, compilation, out var ambiguityWinner, expected[count]);
AssertEx.Equal("C1.operator checked " + op + @"(int)", ambiguityWinner.ToDisplayString());
Assert.Equal(2, actualSymbols.Length);
AssertEx.Equal("C1.operator checked " + op + @"(int)", actualSymbols[0].ToDisplayString());
AssertEx.Equal("C1.operator checked " + op + @"(long)", actualSymbols[1].ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01220_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + ToCRefOp(op) + @"(int)""/>.
/// </summary>
class C1
{
public void operator " + op + @"(long x) {}
public void operator checked " + op + @"(long x) {}
public void operator " + op + @"(int x) {}
public void operator checked " + op + @"(int x) {}
}
/// <summary>
/// See <see cref=""C1.operator checked " + ToCRefOp(op) + @"(int)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator checked " + op + @"(int)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01230_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + ToCRefOp(op) + @"(string)""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator checked " + op + @"(int x) {}
public void operator " + op + @"(long x) {}
public void operator checked " + op + @"(long x) {}
}
#line 11
/// <summary>
/// See <see cref=""C1.operator checked " + ToCRefOp(op) + @"(string)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator checked +=(string)' that could not be resolved
// /// See <see cref="operator checked +=(string)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator checked " + ToCRefOp(op) + @"(string)").WithArguments("operator checked " + ToCRefOp(op) + @"(string)").WithLocation(3, 20),
// (12,20): warning CS1574: XML comment has cref attribute 'operator checked +=(string)' that could not be resolved
// /// See <see cref="C1.operator checked +=(string)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + ToCRefOp(op) + @"(string)").WithArguments("operator checked " + ToCRefOp(op) + @"(string)").WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01231_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + ToCRefOp(op) + @"(int, string)""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator checked " + op + @"(int x) {}
public void operator " + op + @"(long x) {}
public void operator checked " + op + @"(long x) {}
}
#line 11
/// <summary>
/// See <see cref=""C1.operator checked " + ToCRefOp(op) + @"(int, string)""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
var expected = new[] {
// (3,20): warning CS1574: XML comment has cref attribute 'operator checked +=(int, string)' that could not be resolved
// /// See <see cref="operator checked +=(int, string)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "operator checked " + ToCRefOp(op) + @"(int, string)").WithArguments("operator checked " + ToCRefOp(op) + @"(int, string)").WithLocation(3, 20),
// (12,20): warning CS1574: XML comment has cref attribute 'operator checked +=(int, string)' that could not be resolved
// /// See <see cref="C1.operator checked +=(int, string)"/>.
Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + ToCRefOp(op) + @"(int, string)").WithArguments("operator checked " + ToCRefOp(op) + @"(int, string)").WithLocation(12, 20)
};
compilation.VerifyDiagnostics(expected);
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]);
Assert.Null(actualSymbol);
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void CompoundAssignment_01240_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""operator checked " + ToCRefOp(op) + @"""/>.
/// </summary>
class C1
{
public static void " + CompoundAssignmentOperatorName(op, isChecked: true) + @"(int x, long y) {}
}
/// <summary>
/// See <see cref=""C1.operator checked " + ToCRefOp(op) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1." + CompoundAssignmentOperatorName(op, isChecked: true) + @"(int, long)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/78103")]
public void CompoundAssignment_01260_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
/// <summary>
/// See <see cref=""" + CompoundAssignmentOperatorName(op, isChecked: true) + @"""/>.
/// </summary>
class C1
{
public void operator " + op + @"(int x) {}
public void operator checked " + op + @"(int x) {}
}
/// <summary>
/// See <see cref=""C1." + CompoundAssignmentOperatorName(op, isChecked: true) + @"""/>.
/// </summary>
class C2
{}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose));
compilation.VerifyDiagnostics();
int count = 0;
foreach (var crefSyntax in GetCrefSyntaxes(compilation))
{
var actualSymbol = GetReferencedSymbol(crefSyntax, compilation);
AssertEx.Equal("C1.operator checked " + op + @"(int)", actualSymbol.ToDisplayString());
count++;
}
Assert.Equal(2, count);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01280_Readonly([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
struct S1
{
public readonly void operator " + op + @"(int x) {}
public readonly void operator checked " + op + @"(int x) {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01281_Readonly([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
struct S1
{
public readonly void operator " + op + @"(int x) {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01290_Readonly([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
readonly struct S1
{
public void operator " + op + @"(int x) {}
public void operator checked " + op + @"(int x) {}
}
readonly struct S2
{
public readonly void operator " + op + @"(int x) {}
public readonly void operator checked " + op + @"(int x) {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("S2").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01291_Readonly([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
readonly struct S1
{
public void operator " + op + @"(int x) {}
}
readonly struct S2
{
public readonly void operator " + op + @"(int x) {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("S2").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01300_Readonly([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
struct S1
{
int F;
public readonly void operator " + op + @"(int x) { F++; }
public readonly void operator checked " + op + @"(int x) { F++; }
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (5,47): error CS1604: Cannot assign to 'F' because it is read-only
// public readonly void operator +=(int x) { F++; }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(5, 47),
// (6,55): error CS1604: Cannot assign to 'F' because it is read-only
// public readonly void operator checked +=(int x) { F++; }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(6, 55)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01301_Readonly([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
struct S1
{
int F;
public readonly void operator " + op + @"(int x)
{
F++;
}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (7,9): error CS1604: Cannot assign to 'F' because it is read-only
// F++;
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(7, 9)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01310_Readonly([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
readonly struct S1
{
public void operator " + op + @"(int x) { this = new S1(); }
public void operator checked " + op + @"(int x) { this = new S1(); }
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (4,38): error CS1604: Cannot assign to 'this' because it is read-only
// public void operator +=(int x) { this = new S1(); }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(4, 38),
// (5,46): error CS1604: Cannot assign to 'this' because it is read-only
// public void operator checked +=(int x) { this = new S1(); }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(5, 46)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01311_Readonly([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
readonly struct S1
{
public void operator " + op + @"(int x)
{
this = new S1();
}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (6,9): error CS1604: Cannot assign to 'this' because it is read-only
// this = new S1();
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(6, 9)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01330_Readonly([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
void operator checked " + op + @"(int x);
}
struct S1 : I1
{
readonly void I1.operator " + op + @"(int x) {}
readonly void I1.operator checked " + op + @"(int x) {}
}
readonly struct S2 : I1
{
readonly void I1.operator " + op + @"(int x) {}
readonly void I1.operator checked " + op + @"(int x) {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("S2").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01331_Readonly([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
struct S1 : I1
{
readonly void I1.operator " + op + @"(int x) {}
}
readonly struct S2 : I1
{
readonly void I1.operator " + op + @"(int x) {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("S2").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01340_Readonly([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
void operator checked " + op + @"(int x);
}
readonly struct S1 : I1
{
void I1.operator " + op + @"(int x) {}
void I1.operator checked " + op + @"(int x) {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01341_Readonly([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
readonly struct S1 : I1
{
void I1.operator " + op + @"(int x) {}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyEmitDiagnostics();
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01350_Readonly([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
void operator checked " + op + @"(int x);
}
struct S1 : I1
{
int F;
readonly void I1.operator " + op + @"(int x) { F++; }
readonly void I1.operator checked " + op + @"(int x) { F++; }
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (11,43): error CS1604: Cannot assign to 'F' because it is read-only
// readonly void I1.operator +=(int x) { F++; }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(11, 43),
// (12,51): error CS1604: Cannot assign to 'F' because it is read-only
// readonly void I1.operator checked +=(int x) { F++; }
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(12, 51)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01351_Readonly([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
struct S1 : I1
{
int F;
readonly void I1.operator " + op + @"(int x)
{
F++;
}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (12,9): error CS1604: Cannot assign to 'F' because it is read-only
// F++;
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "F").WithArguments("F").WithLocation(12, 9)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.True(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01360_Readonly([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
void operator checked " + op + @"(int x);
}
readonly struct S1 : I1
{
void I1.operator " + op + @"(int x)
{
this = new S1();
}
void I1.operator checked " + op + @"(int x)
{
this = new S1();
}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (12,9): error CS1604: Cannot assign to 'this' because it is read-only
// this = new S1();
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(12, 9),
// (17,9): error CS1604: Cannot assign to 'this' because it is read-only
// this = new S1();
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(17, 9)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01361_Readonly([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
readonly struct S1 : I1
{
void I1.operator " + op + @"(int x)
{
this = new S1();
}
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
compilation.VerifyDiagnostics(
// (11,9): error CS1604: Cannot assign to 'this' because it is read-only
// this = new S1();
Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(11, 9)
);
foreach (var m in compilation.GetTypeByMetadataName("S1").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.True(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01370_Readonly([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
void operator checked " + op + @"(int x);
}
interface I2 : I1
{
#line 200
readonly void I1.operator " + op + @"(int x) {}
readonly void I1.operator checked " + op + @"(int x) {}
}
class C3 : I1
{
#line 300
readonly void I1.operator " + op + @"(int x) {}
readonly void I1.operator checked " + op + @"(int x) {}
}
class C4
{
#line 400
public readonly void operator " + op + @"(int x) {}
public readonly void operator checked " + op + @"(int x) {}
}
interface I5
{
#line 500
readonly void operator " + op + @"(int x);
readonly void operator checked " + op + @"(int x);
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
compilation.VerifyDiagnostics(
// (200,31): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(200, 31),
// (201,39): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(201, 39),
// (300,31): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(300, 31),
// (301,39): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(301, 39),
// (400,35): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly void operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(400, 35),
// (401,43): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly void operator checked +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(401, 43),
// (500,28): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void operator +=(int x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(500, 28),
// (501,36): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void operator checked +=(int x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(501, 36)
);
foreach (var m in compilation.GetTypeByMetadataName("I2").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("C3").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("C4").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("I5").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01371_Readonly([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
interface I1
{
void operator " + op + @"(int x);
}
interface I2 : I1
{
#line 200
readonly void I1.operator " + op + @"(int x) {}
}
class C3 : I1
{
#line 300
readonly void I1.operator " + op + @"(int x) {}
}
class C4
{
#line 400
public readonly void operator " + op + @"(int x) {}
}
interface I5
{
#line 500
readonly void operator " + op + @"(int x);
}
";
var compilation = CreateCompilation([source, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net90);
compilation.VerifyDiagnostics(
// (200,31): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(200, 31),
// (300,31): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void I1.operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(300, 31),
// (400,35): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly void operator +=(int x) {}
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(400, 35),
// (500,28): error CS0106: The modifier 'readonly' is not valid for this item
// readonly void operator +=(int x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, op).WithArguments("readonly").WithLocation(500, 28)
);
foreach (var m in compilation.GetTypeByMetadataName("I2").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("C3").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("C4").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
foreach (var m in compilation.GetTypeByMetadataName("I5").GetMembers().OfType<MethodSymbol>().Where(m => !m.IsConstructor()))
{
Assert.False(m.IsDeclaredReadOnly);
Assert.False(m.IsEffectivelyReadOnly);
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01380_VisualBasic([CombinatorialValues("+=", "-=", "*=", "/=", "<<=", ">>=")] string op)
{
var source1 = @"
public class C1
{
public void operator " + op + @"(int x) {}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
var source2 = @"
Public Module Program
Public Sub Main()
Dim c1 = New C1()
c1 " + op + @" 1
End Sub
End Module
";
CreateVisualBasicCompilation("Program", source2, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// error BC30452: Operator '+' is not defined for types 'C1' and 'Integer'.
Diagnostic(30452 /*ERRID.ERR_BinaryOperands3*/, "c1 " + op + @" 1").WithArguments(op[..^1], "C1", "Integer").WithLocation(5, 9)
);
string opName = CompoundAssignmentOperatorName(op, isChecked: false);
var source3 = @"
Public Module Program
Public Sub Main()
Dim c1 = New C1()
c1." + opName + @"(1)
End Sub
End Module
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Public Overloads Sub op_AdditionAssignment(x As Integer)' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Public Overloads Sub " + opName + @"(x As Integer)", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 12)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01381_VisualBasic([CombinatorialValues("%=", "&=", "|=", "^=", ">>>=")] string op)
{
var source1 = @"
public class C1
{
public void operator " + op + @"(int x) {}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
string opName = CompoundAssignmentOperatorName(op, isChecked: false);
var source3 = @"
Public Module Program
Public Sub Main()
Dim c1 = New C1()
c1." + opName + @"(1)
End Sub
End Module
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Public Overloads Sub op_BitwiseOrAssignment(x As Integer)' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Public Overloads Sub " + opName + @"(x As Integer)", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 12)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01390_VisualBasic([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source1 = @"
public interface I1
{
public void operator " + op + @"(int x);
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
string opName = CompoundAssignmentOperatorName(op, isChecked: false);
var source3 = @"
Public Class Program
Implements I1
Public Sub " + opName + @"(x As Integer) Implements I1." + opName + @"
End Sub
End Class
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Sub op_BitwiseOrAssignment(x As Integer)' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Sub " + opName + @"(x As Integer)", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 16)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01400_VisualBasic_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source1 = @"
public interface I1
{
public sealed void operator " + op + @"(int x) {}
public void operator checked " + op + @"(int x);
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
string opName = CompoundAssignmentOperatorName(op, isChecked: true);
var source3 = @"
Public Class Program
Implements I1
Public Sub " + opName + @"(x As Integer) Implements I1." + opName + @"
End Sub
End Class
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Sub op_BitwiseOrAssignment(x As Integer)' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Sub " + opName + @"(x As Integer)", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 16)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01410_VisualBasic([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source1 = @"
public abstract class C1
{
public abstract void operator " + op + @"(int x);
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
string opName = CompoundAssignmentOperatorName(op, isChecked: false);
var source3 = @"
Public Class Program
Inherits C1
Public Overrides Sub " + opName + @"(x As Integer)
End Sub
End Class
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Public MustOverride Overloads Sub op_BitwiseOrAssignment(x As Integer)' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Public MustOverride Overloads Sub " + opName + @"(x As Integer)", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 26)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01420_VisualBasic_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op)
{
var source1 = @"
public abstract class C1
{
public void operator " + op + @"(int x) {}
public abstract void operator checked " + op + @"(int x);
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute], targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics();
string opName = CompoundAssignmentOperatorName(op, isChecked: true);
var source3 = @"
Public Class Program
Inherits C1
Public Overrides Sub " + opName + @"(x As Integer)
End Sub
End Class
";
CreateVisualBasicCompilation("Program", source3, referencedCompilations: new[] { comp1 }, referencedAssemblies: comp1.References).VerifyDiagnostics(
// BC37319: 'Public MustOverride Overloads Sub op_BitwiseOrAssignment(x As Integer)' requires compiler feature 'UserDefinedCompoundAssignmentOperators', which is not supported by this version of the Visual Basic compiler.
Diagnostic(37319 /*ERRID.ERR_UnsupportedCompilerFeature*/, opName).WithArguments("Public MustOverride Overloads Sub " + opName + @"(x As Integer)", "UserDefinedCompoundAssignmentOperators").WithLocation(5, 26)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01430_MetadataValidation([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool isChecked)
{
if (isChecked && !CompoundAssignmentOperatorHasCheckedForm(op))
{
return;
}
string name = CompoundAssignmentOperatorName(op, isChecked);
var source1 = @"
using System.Runtime.CompilerServices;
public class C1
{
[SpecialName]
public void " + name + @"(int x) {}
}
public class C2
{
[SpecialName]
public int " + name + @"(int x) => 0; // Not void returning
}
public class C3
{
[SpecialName]
public void " + name + @"(int a, int x = 0) {} // Has two parameters
}
public class C4
{
[SpecialName]
public void " + name + @"(params int[] x) {} // Has params
}
public class C5
{
[SpecialName]
public void " + name + @"(__arglist) {} // Is vararg
}
public class C6
{
[SpecialName]
public void " + name + @"<T>(T x) {} // Generic
}
public class C7
{
[SpecialName]
public void " + name + @"() {} // Has no parameter
}
public class C8
{
[SpecialName]
public void " + name + @"(int a, params int[] x) {} // Has params
}
public class C9
{
[SpecialName]
public void " + name + @"(int a, __arglist) {} // Is vararg
}
public class C10
{
[SpecialName]
public void " + name + @"(in decimal x) {}
}
";
var comp1 = CreateCompilation(source1);
var comp1Ref = comp1.EmitToImageReference();
var comp2 = CreateCompilation("", references: [comp1Ref]);
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C1." + name),
MethodKind.UserDefinedOperator,
"C1.operator " + (isChecked ? "checked " : "") + op + "(int)");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C2." + name),
MethodKind.Ordinary,
"C2." + name + "(int)");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C3." + name),
MethodKind.Ordinary,
"C3." + name + "(int, int)");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C4." + name),
MethodKind.Ordinary,
"C4." + name + "(params int[])");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C5." + name),
MethodKind.Ordinary,
"C5." + name + "(__arglist)");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C6." + name),
MethodKind.Ordinary,
"C6." + name + "<T>(T)");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C7." + name),
MethodKind.Ordinary,
"C7." + name + "()");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C8." + name),
MethodKind.Ordinary,
"C8." + name + "(int, params int[])");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C9." + name),
MethodKind.Ordinary,
"C9." + name + "(int, __arglist)");
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C10." + name),
MethodKind.UserDefinedOperator,
"C10.operator " + (isChecked ? "checked " : "") + op + "(in decimal)");
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01431_MetadataValidation(
[CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op,
[CombinatorialValues("ref", "ref readonly", "out")] string refModifier,
bool isChecked)
{
if (isChecked && !CompoundAssignmentOperatorHasCheckedForm(op))
{
return;
}
string name = CompoundAssignmentOperatorName(op, isChecked);
var source1 = @"
using System.Runtime.CompilerServices;
public abstract class C1
{
[SpecialName]
public abstract void " + name + @"(" + refModifier + @" int x);
}
";
var comp1 = CreateCompilation(source1);
var comp1Ref = comp1.EmitToImageReference();
var comp2 = CreateCompilation("", references: [comp1Ref]);
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C1." + name),
MethodKind.Ordinary,
"C1." + name + "(" + refModifier + " int)");
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01440_MetadataValidation(
[CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op,
[CombinatorialValues("private", "protected", "private protected", "internal", "internal protected")] string accessibility,
bool isChecked)
{
if (isChecked && !CompoundAssignmentOperatorHasCheckedForm(op))
{
return;
}
string name = CompoundAssignmentOperatorName(op, isChecked);
var source1 = @"
using System.Runtime.CompilerServices;
public class C1
{
[SpecialName]
" + accessibility + @" void " + name + @"(int x) {}
}
";
var comp1 = CreateCompilation(source1);
var comp1Ref = comp1.EmitToImageReference();
var comp2 = CreateCompilation("", references: [comp1Ref], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
AssertMetadataSymbol(comp2.GetMember<MethodSymbol>("C1." + name),
MethodKind.Ordinary,
"C1." + name + "(int)");
}
[Fact]
public void CompoundAssignment_01450_Consumption_OverloadResolutionPriority()
{
var source1 = @"
using System.Runtime.CompilerServices;
public class C1
{
public void operator +=(int x)
{
System.Console.Write(""[intC1]"");
}
[OverloadResolutionPriority(1)]
public void operator +=(long x)
{
System.Console.Write(""[longC1]"");
}
}
public class C2
{
public static C2 operator +(C2 a, int x)
{
System.Console.Write(""[intC2]"");
return a;
}
[OverloadResolutionPriority(1)]
public static C2 operator +(C2 a, long x)
{
System.Console.Write(""[longC2]"");
return a;
}
}
public class Program
{
static void Main()
{
var x = new C1();
x += 1;
var y = new C2();
y += 1;
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute, OverloadResolutionPriorityAttributeDefinition], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "[longC1][longC2]").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_01460_Consumption_OverloadResolutionPriority_CheckedContext()
{
var source1 = @"
using System.Runtime.CompilerServices;
public class C1
{
public void operator +=(int x)
{
System.Console.Write(""[intC1]"");
}
public void operator checked +=(int x)
{
System.Console.Write(""[intC1Checked]"");
}
[OverloadResolutionPriority(1)]
public void operator +=(long x)
{
System.Console.Write(""[longC1]"");
}
}
public class C2
{
public static C2 operator +(C2 a, int x)
{
System.Console.Write(""[intC2]"");
return a;
}
public static C2 operator checked +(C2 a, int x)
{
System.Console.Write(""[intC2Checked]"");
return a;
}
[OverloadResolutionPriority(1)]
public static C2 operator +(C2 a, long x)
{
System.Console.Write(""[longC2]"");
return a;
}
}
public class Program
{
static void Main()
{
checked
{
var x = new C1();
x += 1;
var y = new C2();
y += 1;
}
}
}
";
var comp1 = CreateCompilation([source1, CompilerFeatureRequiredAttribute, OverloadResolutionPriorityAttributeDefinition], options: TestOptions.DebugExe);
CompileAndVerify(comp1, expectedOutput: "[longC1][longC2]").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_01470_InterpolatedStringHandlerArgument()
{
var code = @"
using System.Runtime.CompilerServices;
internal struct StructLogger
{
public void operator +=([InterpolatedStringHandlerArgument("""")] DummyHandler handler)
{
}
public static StructLogger operator -(StructLogger s, [InterpolatedStringHandlerArgument(""s"")] DummyHandler handler)
{
return s;
}
void Test1()
{
this+=$""log:{0}"";
}
void Test2()
{
this-=$""log:{0}"";
}
}
[InterpolatedStringHandler]
internal ref struct DummyHandler
{
public DummyHandler(int literalLength, int formattedCount, StructLogger structLogger)
{
}
public string GetContent() => null;
public void AppendLiteral(string s) {}
public void AppendFormatted<T>(T t) {}
}
";
var comp = CreateCompilation(new[] { code, CompilerFeatureRequiredAttribute, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute });
comp.VerifyDiagnostics(
// 0.cs(17,15): error CS7036: There is no argument given that corresponds to the required parameter 'structLogger' of 'DummyHandler.DummyHandler(int, int, StructLogger)'
// this+=$"log:{0}";
Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, @"$""log:{0}""").WithArguments("structLogger", "DummyHandler.DummyHandler(int, int, StructLogger)").WithLocation(17, 15),
// 0.cs(17,15): error CS1615: Argument 3 may not be passed with the 'out' keyword
// this+=$"log:{0}";
Diagnostic(ErrorCode.ERR_BadArgExtraRef, @"$""log:{0}""").WithArguments("3", "out").WithLocation(17, 15),
// 0.cs(21,15): error CS7036: There is no argument given that corresponds to the required parameter 'structLogger' of 'DummyHandler.DummyHandler(int, int, StructLogger)'
// this-=$"log:{0}";
Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, @"$""log:{0}""").WithArguments("structLogger", "DummyHandler.DummyHandler(int, int, StructLogger)").WithLocation(21, 15),
// 0.cs(21,15): error CS1615: Argument 3 may not be passed with the 'out' keyword
// this-=$"log:{0}";
Diagnostic(ErrorCode.ERR_BadArgExtraRef, @"$""log:{0}""").WithArguments("3", "out").WithLocation(21, 15)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01480_GetOperatorKind([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
SyntaxKind kind = CompoundAssignmentOperatorTokenKind(op);
string name = OperatorFacts.CompoundAssignmentOperatorNameFromSyntaxKind(kind, isChecked: false);
Assert.Equal(kind, SyntaxFacts.GetOperatorKind(name));
if (CompoundAssignmentOperatorHasCheckedForm(op))
{
name = OperatorFacts.CompoundAssignmentOperatorNameFromSyntaxKind(kind, isChecked: true);
Assert.Equal(kind, SyntaxFacts.GetOperatorKind(name));
}
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01490_ExpressionTree([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
using System.Linq.Expressions;
public class C1
{
public void operator" + op + @"(int x)
{
}
}
public class Program
{
static void Main()
{
Expression<System.Action<C1>> x = (c1) => c1 " + op + @" 1;
}
}
";
var comp2 = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp2.VerifyDiagnostics(
// (15,51): error CS0832: An expression tree may not contain an assignment operator
// Expression<System.Action<C1>> x = (c1) => c1 += 1;
Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAssignment, "c1 " + op + " 1").WithLocation(15, 51)
);
}
[Theory]
[CombinatorialData]
public void CompoundAssignment_01500_Partial([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op)
{
var source = @"
partial class C1
{
public partial void operator" + op + @"(int x);
public partial void M(int x)
{
}
public partial void M(int x);
}
";
var comp2 = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp2.VerifyDiagnostics(
// (4,20): error CS1519: Invalid token 'void' in a member declaration
// public partial void operator+=(int x);
Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "void").WithArguments("void").WithLocation(4, 20),
// (4,33): error CS9308: User-defined operator 'C1.operator +=(int)' must be declared public
// public partial void operator+=(int x);
Diagnostic(ErrorCode.ERR_OperatorsMustBePublic, op).WithArguments("C1.operator " + op + "(int)").WithLocation(4, 33),
// (4,33): error CS0501: 'C1.operator +=(int)' must declare a body because it is not marked abstract, extern, or partial
// public partial void operator+=(int x);
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, op).WithArguments("C1.operator " + op + "(int)").WithLocation(4, 33)
);
}
[Fact]
public void CompoundAssignment_01510_CopyModifiers()
{
/*
public class C1
{
public virtual void modopt(int64) operator +=(int x) {}
}
*/
var ilSource = @"
.class public auto ansi beforefieldinit C1
extends System.Object
{
.method public hidebysig specialname newslot virtual
instance void modopt(int64) op_AdditionAssignment (int32 x) cil managed
{
// Method begins at RVA 0x2069
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void System.Object::.ctor()
IL_0006: nop
IL_0007: ret
}
}
";
var source1 =
@"
public class C2 : C1
{
public override void operator +=(int y)
{
System.Console.Write(""C2"");
}
static void Main()
{
C1 c1 = new C2();
c1 += 1;
}
}
";
var compilation1 = CreateCompilationWithIL([source1, CompilerFeatureRequiredAttribute], ilSource, options: TestOptions.DebugExe);
CompileAndVerify(compilation1, symbolValidator: verify, sourceSymbolValidator: verify, expectedOutput: "C2").VerifyDiagnostics();
void verify(ModuleSymbol m)
{
AssertEx.Equal("void modopt(System.Int64) C2.op_AdditionAssignment(System.Int32 y)", m.GlobalNamespace.GetMember("C2.op_AdditionAssignment").ToTestDisplayString());
}
}
[Fact]
public void CompoundAssignment_01520_CopyModifiers()
{
/*
public class C1
{
public virtual void operator +=(int modopt(int64) x) {}
}
*/
var ilSource = @"
.class public auto ansi beforefieldinit C1
extends System.Object
{
.method public hidebysig specialname newslot virtual
instance void op_AdditionAssignment (int32 modopt(int64) x) cil managed
{
// Method begins at RVA 0x2069
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void System.Object::.ctor()
IL_0006: nop
IL_0007: ret
}
}
";
var source1 =
@"
public class C2 : C1
{
public override void operator +=(int y)
{
System.Console.Write(""C2"");
}
static void Main()
{
C1 c1 = new C2();
c1 += 1;
}
}
";
var compilation1 = CreateCompilationWithIL([source1, CompilerFeatureRequiredAttribute], ilSource, options: TestOptions.DebugExe);
CompileAndVerify(compilation1, symbolValidator: verify, sourceSymbolValidator: verify, expectedOutput: "C2").VerifyDiagnostics();
void verify(ModuleSymbol m)
{
AssertEx.Equal("void C2.op_AdditionAssignment(System.Int32 modopt(System.Int64) y)", m.GlobalNamespace.GetMember("C2.op_AdditionAssignment").ToTestDisplayString());
}
}
[Fact]
public void CompoundAssignment_01530_NullableMismatchOnOverride()
{
var source = @"
#nullable enable
abstract class C1
{
public abstract void operator +=(string? x);
public abstract void operator -=(string x);
}
class C2 : C1
{
public override void operator +=(string x) {}
public override void operator -=(string? x) {}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (12,35): warning CS8765: Nullability of type of parameter 'x' doesn't match overridden member (possibly because of nullability attributes).
// public override void operator +=(string x) {}
Diagnostic(ErrorCode.WRN_TopLevelNullabilityMismatchInParameterTypeOnOverride, "+=").WithArguments("x").WithLocation(12, 35)
);
}
[Fact]
public void CompoundAssignment_01540_Consumption_InCatchFilter()
{
var source = @"
public struct C1
{
public int _F;
public void operator +=(int x)
{
System.Console.Write(""+="");
_F += x;
}
public void operator -=(int x)
{
System.Console.Write(""-="");
_F -= x;
}
public static implicit operator bool (C1 x) => x._F % 2 == 0;
}
public class Program
{
static void Main()
{
C1 x = new C1();
try
{
try
{
throw null;
}
catch when (x += 3)
{
System.Console.Write(""!"");
}
}
catch when (x -= 1)
{
System.Console.Write(x._F);
}
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp, expectedOutput: "+=-=2").VerifyDiagnostics();
verifier.VerifyIL("Program.Main",
@"
{
// Code size 104 (0x68)
.maxstack 2
.locals init (C1 V_0, //x
bool V_1,
C1 V_2,
bool V_3)
IL_0000: nop
IL_0001: ldloca.s V_0
IL_0003: initobj ""C1""
.try
{
IL_0009: nop
.try
{
IL_000a: nop
IL_000b: ldnull
IL_000c: throw
}
filter
{
IL_000d: pop
IL_000e: ldloc.0
IL_000f: stloc.2
IL_0010: ldloca.s V_2
IL_0012: ldc.i4.3
IL_0013: call ""void C1.op_AdditionAssignment(int)""
IL_0018: nop
IL_0019: ldloc.2
IL_001a: stloc.0
IL_001b: ldloc.2
IL_001c: call ""bool C1.op_Implicit(C1)""
IL_0021: stloc.1
IL_0022: ldloc.1
IL_0023: ldc.i4.0
IL_0024: cgt.un
IL_0026: endfilter
} // end filter
{ // handler
IL_0028: pop
IL_0029: nop
IL_002a: ldstr ""!""
IL_002f: call ""void System.Console.Write(string)""
IL_0034: nop
IL_0035: nop
IL_0036: leave.s IL_0038
}
IL_0038: nop
IL_0039: leave.s IL_0067
}
filter
{
IL_003b: pop
IL_003c: ldloc.0
IL_003d: stloc.2
IL_003e: ldloca.s V_2
IL_0040: ldc.i4.1
IL_0041: call ""void C1.op_SubtractionAssignment(int)""
IL_0046: nop
IL_0047: ldloc.2
IL_0048: stloc.0
IL_0049: ldloc.2
IL_004a: call ""bool C1.op_Implicit(C1)""
IL_004f: stloc.3
IL_0050: ldloc.3
IL_0051: ldc.i4.0
IL_0052: cgt.un
IL_0054: endfilter
} // end filter
{ // handler
IL_0056: pop
IL_0057: nop
IL_0058: ldloc.0
IL_0059: ldfld ""int C1._F""
IL_005e: call ""void System.Console.Write(int)""
IL_0063: nop
IL_0064: nop
IL_0065: leave.s IL_0067
}
IL_0067: ret
}
");
}
[Fact]
public void CompoundAssignment_01550_Consumption_ConditionalAccessTarget()
{
var source = @"
class C1
{
public int _F;
public void operator +=(int x)
{
System.Console.Write(""+="");
_F += x;
}
}
class C2
{
public C1 _F;
}
class Program
{
static void Main()
{
C2 x = new C2() { _F = new C1()};
x?._F += 3;
System.Console.Write(x._F._F);
var result = x?._F += 2;
System.Console.Write(result._F);
System.Console.Write(x._F._F);
x = null;
x?._F += 1;
result = x?._F += 2;
System.Console.Write(result is null ? ""null"" : ""!"");
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp, expectedOutput: "+=3+=55null").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_01560_Consumption_ConditionalAccessTarget()
{
var source = @"
struct S1
{
public int _F;
public void operator +=(int x)
{
System.Console.Write(""+="");
_F += x;
}
}
class C2
{
public S1 _F;
}
class Program
{
static void Main()
{
C2 x = new C2();
x?._F += 3;
System.Console.Write(x._F._F);
var result = x?._F += 2;
System.Console.Write(result.GetValueOrDefault()._F);
System.Console.Write(x._F._F);
x = null;
x?._F += 1;
result = x?._F += 2;
System.Console.Write(result is null ? ""null"" : ""!"");
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp, expectedOutput: "+=3+=55null").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_01570_Consumption_ConditionalAccessTarget()
{
var source = @"
interface I1
{
public int F {get;}
public void operator +=(int x);
}
struct S1 : I1
{
public int _F;
public void operator +=(int x)
{
System.Console.Write(""+="");
_F += x;
}
public int F => _F;
}
struct C1 : I1
{
public int _F;
public void operator +=(int x)
{
System.Console.Write(""+="");
_F += x;
}
public int F => _F;
}
class C2<T> where T : I1, new()
{
public T _F;
}
class Program
{
static void Main()
{
Test<S1>();
Test<C1>();
}
static void Test<T>() where T : I1, new()
{
C2<T> x = new C2<T>() { _F = new T() };
x?._F += 3;
System.Console.Write(x._F.F);
x = null;
x?._F += 1;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp, expectedOutput: "+=3+=3").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_01580_Consumption_ConditionalAccessTarget()
{
var source = @"
interface I1
{
public void operator +=(int x);
}
class C2<T> where T : I1
{
public T _F;
}
class Program
{
static void Test<T>(C2<T> x) where T : I1
{
var result = x?._F += 2;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute]);
comp.VerifyDiagnostics(
// (16,24): error CS8978: 'T' cannot be made nullable.
// var result = x?._F += 2;
Diagnostic(ErrorCode.ERR_CannotBeMadeNullable, "._F += 2").WithArguments("T").WithLocation(16, 24)
);
}
[Fact]
public void CompoundAssignment_01590_Consumption_ConditionalAccessTarget()
{
var source = @"
using System.Threading.Tasks;
struct S1
{
public int _F;
public void operator +=(int x)
{
System.Console.Write(""+="");
_F += x;
}
}
class C2
{
public S1 _F;
}
class Program
{
static async Task Main()
{
C2 x = new C2();
x?._F += await GetInt(3);
System.Console.Write(x._F._F);
var result = x?._F += await GetInt(2);
System.Console.Write(result.GetValueOrDefault()._F);
System.Console.Write(x._F._F);
x = null;
x?._F += await GetInt(1);
result = x?._F += await GetInt(2);
System.Console.Write(result is null ? ""null"" : ""!"");
}
static async Task<int> GetInt(int x)
{
await Task.Yield();
return x;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp, expectedOutput: "+=3+=55null").VerifyDiagnostics();
}
[Fact]
public void CompoundAssignment_01600_Consumption_ConditionalAccessTarget()
{
var source = @"
using System.Threading.Tasks;
struct S1
{
public int _F;
public void operator +=(int x)
{
System.Console.Write(""+="");
_F += x;
}
}
class C2
{
public S1 _F;
}
class C3
{
public C2 _F;
}
class Program
{
static async Task Main()
{
C3 x = new C3() { _F = new C2() };
x?._F?._F += await GetInt(3);
System.Console.Write(x._F._F._F);
var result = x?._F?._F += await GetInt(2);
System.Console.Write(result.GetValueOrDefault()._F);
System.Console.Write(x._F._F._F);
x._F = null;
x?._F?._F += await GetInt(1);
result = x?._F?._F += await GetInt(2);
System.Console.Write(result is null ? ""null"" : ""!"");
x = null;
x?._F?._F += await GetInt(1);
result = x?._F?._F += await GetInt(2);
System.Console.Write(result is null ? ""null"" : ""!"");
}
static async Task<int> GetInt(int x)
{
await Task.Yield();
return x;
}
}
";
var comp = CreateCompilation([source, CompilerFeatureRequiredAttribute], options: TestOptions.DebugExe);
var verifier = CompileAndVerify(comp, expectedOutput: "+=3+=55nullnull").VerifyDiagnostics();
}
}
}
|