diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs index 4c0e04302fc1e..84b4a2a0dea69 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs @@ -225,15 +225,24 @@ private ImmutableArray BindOperatorMemberCref(OperatorMemberCrefSyntax s CrefParameterListSyntax? parameterListSyntax = syntax.Parameters; bool isChecked = syntax.CheckedKeyword.IsKind(SyntaxKind.CheckedKeyword); - // NOTE: Prefer binary to unary, unless there is exactly one parameter. - // CONSIDER: we're following dev11 by never using a binary operator name if there's - // exactly one parameter, but doing so would allow us to match single-parameter constructors. SyntaxKind operatorTokenKind = syntax.OperatorToken.Kind(); - string? memberName = parameterListSyntax != null && parameterListSyntax.Parameters.Count == 1 - ? null - : OperatorFacts.BinaryOperatorNameFromSyntaxKindIfAny(operatorTokenKind, isChecked); + string? memberName; - memberName = memberName ?? OperatorFacts.UnaryOperatorNameFromSyntaxKindIfAny(operatorTokenKind, isChecked: isChecked); + if (SyntaxFacts.IsOverloadableCompoundAssignmentOperator(operatorTokenKind)) + { + memberName = OperatorFacts.CompoundAssignmentOperatorNameFromSyntaxKind(operatorTokenKind, isChecked); + } + else + { + // NOTE: Prefer binary to unary, unless there is exactly one parameter. + // CONSIDER: we're following dev11 by never using a binary operator name if there's + // exactly one parameter, but doing so would allow us to match single-parameter constructors. + memberName = parameterListSyntax != null && parameterListSyntax.Parameters.Count == 1 + ? null + : OperatorFacts.BinaryOperatorNameFromSyntaxKindIfAny(operatorTokenKind, isChecked); + + memberName = memberName ?? OperatorFacts.UnaryOperatorNameFromSyntaxKindIfAny(operatorTokenKind, isChecked: isChecked); + } if (memberName == null || (isChecked && !syntax.OperatorToken.IsMissing && !SyntaxFacts.IsCheckedOperator(memberName))) // the operator cannot be checked diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index f47042ebfdb1f..580fc61bf57da 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -2649,7 +2649,7 @@ private BoundExpression BindIncrementOperator(ExpressionSyntax node, ExpressionS } } - ArrayBuilder? LookupUserDefinedInstanceOperators(TypeSymbol lookupInType, string? checkedName, string ordinaryName, int parameterCount, ref CompoundUseSiteInfo useSiteInfo) + private ArrayBuilder? LookupUserDefinedInstanceOperators(TypeSymbol lookupInType, string? checkedName, string ordinaryName, int parameterCount, ref CompoundUseSiteInfo useSiteInfo) { Debug.Assert(parameterCount is 0 or 1); diff --git a/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs b/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs index 544a3e3b5b6ca..077f526471521 100644 --- a/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs @@ -6005,568 +6005,825 @@ public class C1 : C2 comp.VerifyEmitDiagnostics(); } - private static string CompoundAssignmentOperatorName(string op, bool isChecked = false) + [Theory] + [CombinatorialData] + public void Increment_107_CRef([CombinatorialValues("++", "--")] string op) { - var kind = op switch + var source = @" +/// +/// See . +/// +class C1 +{ + public static C1 operator " + op + @"(C1 x) => x; + public void operator " + op + @"() {} +} + +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); + + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) { - ">>=" => SyntaxKind.GreaterThanGreaterThanEqualsToken, - ">>>=" => SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, - _ => SyntaxFactory.ParseToken(op).Kind(), - }; + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation); + AssertEx.Equal("C1.operator " + op + @"()", actualSymbol.ToDisplayString()); + count++; + } - return OperatorFacts.CompoundAssignmentOperatorNameFromSyntaxKind(kind, isChecked: isChecked); + Assert.Equal(2, count); } - 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) + public void Increment_108_CRef([CombinatorialValues("++", "--")] string op) { - string checkedForm = null; + var source = @" +/// +/// See . +/// +class C1 +{ + public void operator " + op + @"() {} +} - if (CompoundAssignmentOperatorHasCheckedForm(op)) - { - checkedForm = @" - public void operator checked" + op + @"(C1 x) {} +/// +/// See . +/// +class C2 +{} "; + var compilation = CreateCompilation(source, 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++; } - var source = -typeKeyword + @" C1 + Assert.Equal(2, count); + } + + [Theory] + [CombinatorialData] + public void Increment_109_CRef([CombinatorialValues("++", "--")] string op) + { + var source = @" +/// +/// See . +/// +class C1 { - public void operator" + op + @"(C1 x) {} -" + checkedForm + @" + public static C1 operator " + op + @"(C1 x) => x; } -"; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net60); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); - - comp = CreateCompilation(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net60); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); - - comp = CreateCompilation(source, 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); +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); - void validate(ModuleSymbol m) + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - if (checkedForm is not null) - { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); - } + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation); + AssertEx.Equal("C1.operator " + op + @"(C1)", actualSymbol.ToDisplayString()); + count++; } - 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.Equal(2, count); } [Theory] [CombinatorialData] - public void CompoundAssignment_00011_HasCheckedForm([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + public void Increment_110_CRef([CombinatorialValues("++", "--")] string op) { - Assert.True(CompoundAssignmentOperatorHasCheckedForm(op)); - - var source = -typeKeyword + @" C1 -{ - public void operator " + op + @"(C1 x) {} - public void operator checked" + op + @"(C1 x) {} -} - -interface I1 + var source = @" +/// +/// See . +/// +class C1 { - public void operator " + op + @"(C1 x) {} - public void operator checked" + op + @"(C1 x) {} + public static C1 operator " + op + @"(C1 x) => x; + public void operator " + op + @"() {} } -" + typeKeyword + @" C2 : I1 -{ - void I1.operator " + op + @"(C1 x) {} - void I1.operator checked" + op + @"(C1 x) {} -} +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net60); - comp.VerifyEmitDiagnostics(); + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + + var expected = new[] { + // (3,20): warning CS0419: Ambiguous reference in cref attribute: 'operator --'. Assuming 'C1.operator --(C1)', but could have also matched other overloads including 'C1.operator --()'. + // /// See . + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "operator " + op).WithArguments("operator " + op, "C1.operator " + op + @"(C1)", "C1.operator " + op + @"()").WithLocation(3, 20), + // (12,20): warning CS0419: Ambiguous reference in cref attribute: 'C1.operator --'. Assuming 'C1.operator --(C1)', but could have also matched other overloads including 'C1.operator --()'. + // /// See . + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "C1.operator " + op).WithArguments("C1.operator " + op, "C1.operator " + op + @"(C1)", "C1.operator " + op + @"()").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 + @"(C1)", ambiguityWinner.ToDisplayString()); + Assert.Equal(2, actualSymbols.Length); + AssertEx.Equal("C1.operator " + op + @"(C1)", actualSymbols[0].ToDisplayString()); + AssertEx.Equal("C1.operator " + op + @"()", actualSymbols[1].ToDisplayString()); + count++; + } + + Assert.Equal(2, count); } [Theory] [CombinatorialData] - public void CompoundAssignment_00012_DoesNotHaveCheckedForm([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + public void Increment_111_CRef([CombinatorialValues("++", "--")] string op) { - Assert.False(CompoundAssignmentOperatorHasCheckedForm(op)); - - var source = -typeKeyword + @" C1 -{ - public void operator checked" + op + @"(C1 x) {} -} - -interface I1 + var source = @" +/// +/// See . +/// +class C1 { - public void operator " + op + @"(C1 x) {} + public void operator " + op + @"() {} + public static C1 operator " + op + @"(C1 x) => x; } -" + typeKeyword + @" C2 : I1 -{ - void I1.operator checked" + op + @"(C1 x) {} -} +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source, 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) - ); + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); - validateOp(comp.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + var expected = new[] { + // (3,20): warning CS0419: Ambiguous reference in cref attribute: 'operator ++'. Assuming 'C1.operator ++()', but could have also matched other overloads including 'C1.operator ++(C1)'. + // /// See . + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "operator " + op).WithArguments("operator " + op, "C1.operator " + op + @"()", "C1.operator " + op + @"(C1)").WithLocation(3, 20), + // (12,20): warning CS0419: Ambiguous reference in cref attribute: 'C1.operator ++'. Assuming 'C1.operator ++()', but could have also matched other overloads including 'C1.operator ++(C1)'. + // /// See . + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "C1.operator " + op).WithArguments("C1.operator " + op, "C1.operator " + op + @"()", "C1.operator " + op + @"(C1)").WithLocation(12, 20) + }; - static void validateOp(MethodSymbol m) + compilation.VerifyDiagnostics(expected); + + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) { - 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); + var actualSymbols = GetReferencedSymbols(crefSyntax, compilation, out var ambiguityWinner, expected[count]); + AssertEx.Equal("C1.operator " + op + @"()", ambiguityWinner.ToDisplayString()); + Assert.Equal(2, actualSymbols.Length); + AssertEx.Equal("C1.operator " + op + @"()", actualSymbols[0].ToDisplayString()); + AssertEx.Equal("C1.operator " + op + @"(C1)", actualSymbols[1].ToDisplayString()); + count++; } + + Assert.Equal(2, count); } [Theory] [CombinatorialData] - public void CompoundAssignment_00013_Not_Static( - [CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, - [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + public void Increment_112_CRef([CombinatorialValues("++", "--")] string op) { - string checkedForm = null; + var source = @" +/// +/// See . +/// +class C1 +{ + public void operator " + op + @"() {} + public static C1 operator " + op + @"(C1 x) => x; +} - if (CompoundAssignmentOperatorHasCheckedForm(op)) - { - checkedForm = @" - public static void operator checked" + op + @"(C1 x) {} +/// +/// See . +/// +class C2 +{} "; + var compilation = CreateCompilation(source, 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++; } - var source = -typeKeyword + @" C1 + Assert.Equal(2, count); + } + + [Theory] + [CombinatorialData] + public void Increment_113_CRef([CombinatorialValues("++", "--")] string op) + { + var source = @" +/// +/// See . +/// +class C1 { - public static void operator" + op + @"(C1 x) {} -" + checkedForm + @" + public void operator " + op + @"() {} } + +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source, 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) - ] - ); + var compilation = CreateCompilation(source, 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 . + 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 . + Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator " + op + @"(C1)").WithArguments("operator " + op + @"(C1)").WithLocation(11, 20) + }; - validate(comp.SourceModule); + compilation.VerifyDiagnostics(expected); - void validate(ModuleSymbol m) + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - if (checkedForm is not null) - { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); - } + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]); + Assert.Null(actualSymbol); + count++; } - 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.Equal(2, count); } [Theory] [CombinatorialData] - public void CompoundAssignment_00014_NotInStaticClass([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + [WorkItem("https://github.com/dotnet/roslyn/issues/78103")] + public void Increment_114_CRef([CombinatorialValues("++", "--")] string op) { - string checkedForm = null; - - if (CompoundAssignmentOperatorHasCheckedForm(op)) - { - checkedForm = @" - public void operator checked" + op + @"(int x) {} -"; - } - var source = @" -static class C1 +/// +/// See . +/// +class C1 { - public void operator" + op + @"(int x) {} -" + checkedForm + @" + public static C1 " + (op == "++" ? WellKnownMemberNames.IncrementOperatorName : WellKnownMemberNames.DecrementOperatorName) + @"() => null; } + +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source); - 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) - ] - ); + var compilation = CreateCompilation(source, 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." + (op == "++" ? WellKnownMemberNames.IncrementOperatorName : WellKnownMemberNames.DecrementOperatorName) + @"()", actualSymbol.ToDisplayString()); + count++; + } + + Assert.Equal(2, count); } [Theory] [CombinatorialData] - public void CompoundAssignment_00020_MustBePublic([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool structure, bool isChecked) + [WorkItem("https://github.com/dotnet/roslyn/issues/78103")] + public void Increment_115_CRef([CombinatorialValues("++", "--")] string op) { - if (isChecked && !CompoundAssignmentOperatorHasCheckedForm(op)) - { - return; - } - - var source = -(structure ? "struct" : "class") + @" C1 + var source = @" +/// +/// See . +/// +class C1 { - void operator " + (isChecked ? "checked " : "") + op + @"(int x) {} -" + (isChecked ? "public void operator " + op + @"(int x) {}" : "") + @" + public static C1 operator " + op + @"(C1 x) => x; } + +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source); - comp.VerifyDiagnostics( - // (3,19): error CS9501: 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)) - ); + var compilation = CreateCompilation(source, 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 CompoundAssignment_00021_ImplicitlyPublicInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + [WorkItem("https://github.com/dotnet/roslyn/issues/78103")] + public void Increment_116_CRef([CombinatorialValues("++", "--")] string op) { - string checkedForm = null; + var source = @" +/// +/// See . +/// +class C1 +{ + public void operator " + op + @"() {} +} - if (CompoundAssignmentOperatorHasCheckedForm(op)) - { - checkedForm = @" - void operator checked" + op + @"(C1 x); +/// +/// See . +/// +class C2 +{} "; + var compilation = CreateCompilation(source, 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 = @" -interface C1 +/// +/// See . +/// +class C1 { - void operator" + op + @"(C1 x); -" + checkedForm + @" + 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 + @"() {} } -"; - var comp = CreateCompilation(source); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); - validate(comp.SourceModule); +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); - void validate(ModuleSymbol m) + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - if (checkedForm is not null) - { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); - } + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation); + AssertEx.Equal("C1.operator checked " + op + @"()", actualSymbol.ToDisplayString()); + count++; } - 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); - } + Assert.Equal(2, count); } [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) + public void Increment_118_CRef_Checked([CombinatorialValues("++", "--")] string op) { - if (isChecked && !CompoundAssignmentOperatorHasCheckedForm(op)) - { - return; - } - - var source = -typeKeyword + @" C1 + var source = @" +/// +/// See . +/// +class C1 { - " + accessibility + @" - void operator " + (isChecked ? "checked " : "") + op + @"(int x) {} -" + (isChecked ? "public void operator " + op + @"(int x) {}" : "") + @" + public void operator " + op + @"() {} + public void operator checked " + op + @"() {} } + +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net60); - comp.VerifyDiagnostics( - // (4,19): error CS9501: 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)) - ); + var compilation = CreateCompilation(source, 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 CompoundAssignment_00030_MustReturnVoid([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + public void Increment_119_CRef_Checked([CombinatorialValues("++", "--")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +/// +/// See . +/// +class C1 { - public C1 operator " + op + @"(int x) => throw null; + public static C1 operator " + op + @"(C1 x) => x; + public static C1 operator checked " + op + @"(C1 x) => x; } + +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - comp.VerifyDiagnostics( - // (3,24): error CS9503: The return type for this operator must be void - // public C1 operator +=(int x) => throw null; - Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, op).WithLocation(3, 24) - ); + var compilation = CreateCompilation(source, 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 CompoundAssignment_00040_MustReturnVoid_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + public void Increment_120_CRef_Checked([CombinatorialValues("++", "--")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +/// +/// See . +/// +class C1 { - public C1 operator checked " + op + @"(int x) => throw null; + 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 +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - comp.VerifyDiagnostics( - // (3,32): error CS9503: 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) - ); + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + + var expected = new[] { + // (3,20): warning CS0419: Ambiguous reference in cref attribute: 'operator checked --'. Assuming 'C1.operator checked --(C1)', but could have also matched other overloads including 'C1.operator checked --()'. + // /// See . + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "operator checked " + op).WithArguments("operator checked " + op, "C1.operator checked " + op + @"(C1)", "C1.operator checked " + op + @"()").WithLocation(3, 20), + // (12,20): warning CS0419: Ambiguous reference in cref attribute: 'C1.operator checked --'. Assuming 'C1.operator checked --(C1)', but could have also matched other overloads including 'C1.operator checked --()'. + // /// See . + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "C1.operator checked " + op).WithArguments("C1.operator checked " + op, "C1.operator checked " + op + @"(C1)", "C1.operator checked " + op + @"()").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 + @"(C1)", ambiguityWinner.ToDisplayString()); + Assert.Equal(2, actualSymbols.Length); + AssertEx.Equal("C1.operator checked " + op + @"(C1)", actualSymbols[0].ToDisplayString()); + AssertEx.Equal("C1.operator checked " + op + @"()", actualSymbols[1].ToDisplayString()); + count++; + } + + Assert.Equal(2, count); } [Theory] [CombinatorialData] - public void CompoundAssignment_00050_WrongNumberOfParameters([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + public void Increment_121_CRef_Checked([CombinatorialValues("++", "--")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +/// +/// See . +/// +class C1 { - public void operator" + op + @"() {} - public void operator" + op + @"(C1 x, C1 y) {} + 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 +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - comp.VerifyDiagnostics( - // (3,25): error CS9506: 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) - ); + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + + var expected = new[] { + // (3,20): warning CS0419: Ambiguous reference in cref attribute: 'operator checked ++'. Assuming 'C1.operator checked ++()', but could have also matched other overloads including 'C1.operator checked ++(C1)'. + // /// See . + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "operator checked " + op).WithArguments("operator checked " + op, "C1.operator checked " + op + @"()", "C1.operator checked " + op + @"(C1)").WithLocation(3, 20), + // (12,20): warning CS0419: Ambiguous reference in cref attribute: 'C1.operator ++'. Assuming 'C1.operator ++()', but could have also matched other overloads including 'C1.operator ++(C1)'. + // /// See . + Diagnostic(ErrorCode.WRN_AmbiguousXMLReference, "C1.operator checked " + op).WithArguments("C1.operator checked " + op, "C1.operator checked " + op + @"()", "C1.operator checked " + op + @"(C1)").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 + @"()", ambiguityWinner.ToDisplayString()); + Assert.Equal(2, actualSymbols.Length); + AssertEx.Equal("C1.operator checked " + op + @"()", actualSymbols[0].ToDisplayString()); + AssertEx.Equal("C1.operator checked " + op + @"(C1)", actualSymbols[1].ToDisplayString()); + count++; + } + + Assert.Equal(2, count); } [Theory] [CombinatorialData] - public void CompoundAssignment_00060_WrongNumberOfParameters_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + public void Increment_122_CRef_Checked([CombinatorialValues("++", "--")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +/// +/// See . +/// +class C1 { - public void operator checked " + op + @"() {} - public void operator checked " + op + @"(C1 x, C1 y) {} + 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; } + +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - comp.VerifyDiagnostics( - // (3,34): error CS9506: 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) - ); + var compilation = CreateCompilation(source, 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 CompoundAssignment_00090_AbstractAllowedInClassAndInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) + public void Increment_123_CRef_Checked([CombinatorialValues("++", "--")] string op) { - string checkedForm = null; + var source = @" +/// +/// See . +/// +class C1 +{ + public void operator " + op + @"() {} + public void operator checked " + op + @"() {} +} - if (CompoundAssignmentOperatorHasCheckedForm(op)) - { - checkedForm = @" - public abstract void operator checked" + op + @"(int x); +#line 10 +/// +/// See . +/// +class C2 +{} "; + var compilation = CreateCompilation(source, 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 . + 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 . + 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++; } - var source = -typeKeyword + @" C1 -{ - public abstract void operator" + op + @"(int x); -" + checkedForm + @" + 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 = @" +/// +/// See . +/// +class C1 +{ + public static C1 " + (op == "++" ? WellKnownMemberNames.CheckedIncrementOperatorName : WellKnownMemberNames.CheckedDecrementOperatorName) + @"() => null; } + +/// +/// See . +/// +class C2 +{} "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); - void validate(ModuleSymbol m) + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - if (checkedForm is not null) - { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); - } + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation); + AssertEx.Equal("C1." + (op == "++" ? WellKnownMemberNames.CheckedIncrementOperatorName : WellKnownMemberNames.CheckedDecrementOperatorName) + @"()", actualSymbol.ToDisplayString()); + count++; } - static void validateOp(MethodSymbol m) + Assert.Equal(2, count); + } + + [Theory] + [CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/78103")] + public void Increment_125_CRef_Checked([CombinatorialValues("++", "--")] string op) + { + var source = @" +/// +/// See . +/// +class C1 +{ + public static C1 operator " + op + @"(C1 x) => x; + public static C1 operator checked " + op + @"(C1 x) => x; +} + +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); + + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) { - 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); + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation); + AssertEx.Equal("C1.operator checked " + op + @"(C1)", actualSymbol.ToDisplayString()); + count++; } - comp = CreateCompilation(["class C2 : C1 {}", source]); - if (typeKeyword == "interface") + 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 = @" +/// +/// See . +/// +class C1 +{ + public void operator " + op + @"() {} + public void operator checked " + op + @"() {} +} + +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); + + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) { - 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) - ] - ); + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation); + AssertEx.Equal("C1.operator checked " + op + @"()", actualSymbol.ToDisplayString()); + count++; } - else + + Assert.Equal(2, count); + } + + private static string CompoundAssignmentOperatorName(string op, bool isChecked = false) + { + var kind = op switch { - 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) - ] - ); - } + ">>=" => SyntaxKind.GreaterThanGreaterThanEqualsToken, + ">>>=" => SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, + _ => SyntaxFactory.ParseToken(op).Kind(), + }; + + return OperatorFacts.CompoundAssignmentOperatorNameFromSyntaxKind(kind, isChecked: isChecked); } + private static bool CompoundAssignmentOperatorHasCheckedForm(string op) => op is "+=" or "-=" or "*=" or "/="; + [Theory] [CombinatorialData] - public void CompoundAssignment_00100_AbstractIsOptionalInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + 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 + @"(int x); + public void operator checked" + op + @"(C1 x) {} "; } - var source = @" -interface C1 + var source = +typeKeyword + @" C1 { - public void operator" + op + @"(int x); + public void operator" + op + @"(C1 x) {} " + checkedForm + @" } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net60); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + + comp = CreateCompilation(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net60); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + + comp = CreateCompilation(source, 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); void validate(ModuleSymbol m) { @@ -6577,12 +6834,12 @@ void validate(ModuleSymbol m) } } - static void validateOp(MethodSymbol m) + 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.IsAbstract); + Assert.Equal(typeKeyword == "interface", m.IsVirtual); Assert.False(m.IsSealed); Assert.False(m.IsOverride); Assert.True(m.HasSpecialName); @@ -6593,187 +6850,240 @@ static void validateOp(MethodSymbol m) [Theory] [CombinatorialData] - public void CompoundAssignment_00110_AbstractCanBeImplementedInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00011_HasCheckedForm([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) { - var source = @" + Assert.True(CompoundAssignmentOperatorHasCheckedForm(op)); + + var source = +typeKeyword + @" C1 +{ + public void operator " + op + @"(C1 x) {} + public void operator checked" + op + @"(C1 x) {} +} + interface I1 { - void operator " + op + @"(int x); + public void operator " + op + @"(C1 x) {} + public void operator checked" + op + @"(C1 x) {} } -interface I3 : I1 +" + typeKeyword + @" C2 : I1 { - void I1.operator " + op + @"(int x) {} + void I1.operator " + op + @"(C1 x) {} + void I1.operator checked" + op + @"(C1 x) {} } "; - bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net60); + comp.VerifyEmitDiagnostics(); + } - if (hasCheckedForm) - { - source += @" -interface I2 + [Theory] + [CombinatorialData] + public void CompoundAssignment_00012_DoesNotHaveCheckedForm([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + { + Assert.False(CompoundAssignmentOperatorHasCheckedForm(op)); + + var source = +typeKeyword + @" C1 { - void operator checked " + op + @"(int x); - sealed void operator " + op + @"(int x) {} + public void operator checked" + op + @"(C1 x) {} } -interface I4 : I2 +interface I1 { - void I2.operator checked " + op + @"(int x) {} + public void operator " + op + @"(C1 x) {} } -class C : I3, I4 -{} -"; - } - else - { - source += @" -class C : I3 -{} +" + typeKeyword + @" C2 : I1 +{ + void I1.operator checked" + op + @"(C1 x) {} +} "; - } - - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + var comp = CreateCompilation(source, 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) + ); - void validate(ModuleSymbol m) - { - validateOp( - m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType().Single(), - "void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)"); - if (hasCheckedForm) - { - validateOp( - m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType().Single(), - "void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)"); - } - } + validateOp(comp.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - static void validateOp(MethodSymbol m, string implements) + static void validateOp(MethodSymbol m) { - Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind); + Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); Assert.False(m.IsStatic); Assert.False(m.IsAbstract); - Assert.False(m.IsVirtual); + Assert.Equal(m.ContainingType.IsInterface, m.IsVirtual); Assert.False(m.IsSealed); Assert.False(m.IsOverride); - Assert.False(m.HasSpecialName); + Assert.True(m.HasSpecialName); Assert.False(m.HasRuntimeSpecialName); - Assert.Equal(Accessibility.Private, m.DeclaredAccessibility); - Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString()); + Assert.Equal(Accessibility.Public, m.DeclaredAccessibility); } } [Theory] [CombinatorialData] - public void CompoundAssignment_00120_AbstractCanBeImplementedExplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) + public void CompoundAssignment_00013_Not_Static( + [CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, + [CombinatorialValues("struct", "class", "interface")] string typeKeyword) { - var source = @" -interface I1 -{ - void operator " + op + @"(int x); -} + string checkedForm = null; -" + typeKeyword + @" C3 : I1 -{ - void I1.operator " + op + @"(int x) {} -} -"; - bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); - - if (hasCheckedForm) + if (CompoundAssignmentOperatorHasCheckedForm(op)) { - source += @" -interface I2 -{ - void operator checked " + op + @"(int x); - sealed void operator " + op + @"(int x) {} -} + checkedForm = @" + public static void operator checked" + op + @"(C1 x) {} +"; + } -" + typeKeyword + @" C4 : I2 + var source = +typeKeyword + @" C1 { - void I2.operator checked " + op + @"(int x) {} + public static void operator" + op + @"(C1 x) {} +" + checkedForm + @" } "; - } + var comp = CreateCompilation(source, 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) + ] + ); - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + validate(comp.SourceModule); void validate(ModuleSymbol m) { - validateOp( - m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType().Where(m => !m.IsConstructor()).Single(), - "void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)"); - if (hasCheckedForm) + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + if (checkedForm is not null) { - validateOp( - m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType().Where(m => !m.IsConstructor()).Single(), - "void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)"); + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); } } - static void validateOp(MethodSymbol m, string implements) + void validateOp(MethodSymbol m) { - Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind); + Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); Assert.False(m.IsStatic); Assert.False(m.IsAbstract); - Assert.False(m.IsVirtual); + Assert.Equal(typeKeyword == "interface", m.IsVirtual); Assert.False(m.IsSealed); Assert.False(m.IsOverride); - Assert.False(m.HasSpecialName); + Assert.True(m.HasSpecialName); Assert.False(m.HasRuntimeSpecialName); - Assert.Equal(Accessibility.Private, m.DeclaredAccessibility); - Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString()); + Assert.Equal(Accessibility.Public, m.DeclaredAccessibility); } } [Theory] [CombinatorialData] - public void CompoundAssignment_00130_AbstractCanBeImplementedImplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) + public void CompoundAssignment_00014_NotInStaticClass([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { + string checkedForm = null; + + if (CompoundAssignmentOperatorHasCheckedForm(op)) + { + checkedForm = @" + public void operator checked" + op + @"(int x) {} +"; + } + var source = @" -interface I1 +static class C1 { - void operator " + op + @"(int x); + public void operator" + op + @"(int x) {} +" + checkedForm + @" } +"; + var comp = CreateCompilation(source); + 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) + ] + ); + } -" + typeKeyword + @" C3 : I1 + [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 { - public void operator " + op + @"(int x) {} + void operator " + (isChecked ? "checked " : "") + op + @"(int x) {} +" + (isChecked ? "public void operator " + op + @"(int x) {}" : "") + @" } "; - bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,19): error CS9501: 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)) + ); + } - if (hasCheckedForm) + [Theory] + [CombinatorialData] + public void CompoundAssignment_00021_ImplicitlyPublicInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + { + string checkedForm = null; + + if (CompoundAssignmentOperatorHasCheckedForm(op)) { - source += @" -interface I2 -{ - void operator checked " + op + @"(int x); - sealed void operator " + op + @"(int x) {} -} + checkedForm = @" + void operator checked" + op + @"(C1 x); +"; + } -" + typeKeyword + @" C4 : I2 + var source = @" +interface C1 { - public void operator checked " + op + @"(int x) {} - public void operator " + op + @"(int x) {} + void operator" + op + @"(C1 x); +" + checkedForm + @" } "; - } + var comp = CreateCompilation(source); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + validate(comp.SourceModule); void validate(ModuleSymbol m) { - validateOp(m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType(). - Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: false)).Single()); - if (hasCheckedForm) + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + if (checkedForm is not null) { - validateOp(m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType(). - Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: true)).Single()); + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); } } @@ -6781,296 +7091,360 @@ static void validateOp(MethodSymbol m) { Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); Assert.False(m.IsStatic); - Assert.False(m.IsAbstract); + Assert.True(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); + Assert.Equal(Accessibility.Public, m.DeclaredAccessibility); } } [Theory] [CombinatorialData] - public void CompoundAssignment_00140_AbstractAllowedOnExplicitImplementationInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + 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) { - var source = @" -interface I1 + if (isChecked && !CompoundAssignmentOperatorHasCheckedForm(op)) + { + return; + } + + var source = +typeKeyword + @" C1 { - void operator " + op + @"(int x) {} + " + accessibility + @" + void operator " + (isChecked ? "checked " : "") + op + @"(int x) {} +" + (isChecked ? "public void operator " + op + @"(int x) {}" : "") + @" } +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net60); + comp.VerifyDiagnostics( + // (4,19): error CS9501: 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)) + ); + } -interface I3 : I1 + [Theory] + [CombinatorialData] + public void CompoundAssignment_00030_MustReturnVoid([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + { + var source = +typeKeyword + @" C1 { - abstract void I1.operator " + op + @"(int x); + public C1 operator " + op + @"(int x) => throw null; } "; - bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + comp.VerifyDiagnostics( + // (3,24): error CS9503: The return type for this operator must be void + // public C1 operator +=(int x) => throw null; + Diagnostic(ErrorCode.ERR_OperatorMustReturnVoid, op).WithLocation(3, 24) + ); + } - if (hasCheckedForm) - { - source += @" -interface I2 + [Theory] + [CombinatorialData] + public void CompoundAssignment_00040_MustReturnVoid_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + { + var source = +typeKeyword + @" C1 { - void operator checked " + op + @"(int x) {} - sealed void operator " + op + @"(int x) {} + public C1 operator checked " + op + @"(int x) => throw null; } +"; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + comp.VerifyDiagnostics( + // (3,32): error CS9503: 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) + ); + } -interface I4 : I2 + [Theory] + [CombinatorialData] + public void CompoundAssignment_00050_WrongNumberOfParameters([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("struct", "class", "interface")] string typeKeyword) + { + var source = +typeKeyword + @" C1 { - abstract void I2.operator checked " + op + @"(int x); + public void operator" + op + @"() {} + public void operator" + op + @"(C1 x, C1 y) {} } "; - } - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); - + comp.VerifyDiagnostics( + // (3,25): error CS9506: 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, targetFramework: TargetFramework.Net90); + comp.VerifyDiagnostics( + // (3,34): error CS9506: 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, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + void validate(ModuleSymbol m) { - validateOp( - m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType().Single(), - "void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)"); - if (hasCheckedForm) + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + if (checkedForm is not null) { - validateOp( - m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType().Single(), - "void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)"); + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); } } - static void validateOp(MethodSymbol m, string implements) + static void validateOp(MethodSymbol m) { - Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind); + Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); Assert.False(m.IsStatic); Assert.True(m.IsAbstract); Assert.False(m.IsVirtual); - Assert.True(m.IsSealed); + Assert.False(m.IsSealed); Assert.False(m.IsOverride); - Assert.False(m.HasSpecialName); + Assert.True(m.HasSpecialName); Assert.False(m.HasRuntimeSpecialName); - Assert.Equal(Accessibility.Private, m.DeclaredAccessibility); - Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString()); + Assert.Equal(Accessibility.Public, m.DeclaredAccessibility); } - 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 C2 : C1 {}", source]); + if (typeKeyword == "interface") { - 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) + 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_00150_AbstractCannotHaveBody([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) + public void CompoundAssignment_00100_AbstractIsOptionalInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { string checkedForm = null; if (CompoundAssignmentOperatorHasCheckedForm(op)) { checkedForm = @" - public abstract void operator checked" + op + @"(int x) {} + public void operator checked" + op + @"(int x); "; } - var source = -typeKeyword + @" C1 + var source = @" +interface C1 { - public abstract void operator" + op + @"(int x) {} + public void operator" + op + @"(int x); " + checkedForm + @" } "; var comp = CreateCompilation(source, 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) - ] - ); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + + void validate(ModuleSymbol m) + { + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + if (checkedForm is not null) + { + validateOp(m.GlobalNamespace.GetMember("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_00160_AbstractExplicitImplementationCannotHaveBody([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00110_AbstractCanBeImplementedInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" interface I1 { - void operator" + op + @"(int x); + void operator " + op + @"(int x); } -interface I2 : I1 +interface I3 : I1 { - abstract void I1.operator" + op + @"(int x) {} + 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, 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) - ); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + + void validate(ModuleSymbol m) + { + validateOp( + m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType().Single(), + "void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)"); + if (hasCheckedForm) + { + validateOp( + m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType().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_00161_AbstractExplicitImplementationCannotHaveBody_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00120_AbstractCanBeImplementedExplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) { var source = @" interface I1 { - sealed void operator" + op + @"(int x) {} - void operator checked" + op + @"(int x); + void operator " + op + @"(int x); } -interface I2 : I1 +" + typeKeyword + @" C3 : I1 { - abstract void I1.operator checked" + op + @"(int x) {} -} -"; - var comp = CreateCompilation(source, 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); + void I1.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); - 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); - 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); - 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, 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 = @" + source += @" interface I2 { void operator checked " + op + @"(int x); @@ -7079,92 +7453,87 @@ interface I2 " + typeKeyword + @" C4 : I2 { - abstract void I2.operator checked " + op + @"(int x) {} + void I2.operator checked " + op + @"(int x) {} } -"; - var comp = CreateCompilation(source, 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, targetFramework: TargetFramework.Net90); CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); void validate(ModuleSymbol m) { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - if (checkedForm is not null) + validateOp( + m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType().Where(m => !m.IsConstructor()).Single(), + "void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)"); + if (hasCheckedForm) { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); + validateOp( + m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType().Where(m => !m.IsConstructor()).Single(), + "void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)"); } } - static void validateOp(MethodSymbol m) + static void validateOp(MethodSymbol m, string implements) { - Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind); Assert.False(m.IsStatic); Assert.False(m.IsAbstract); - Assert.True(m.IsVirtual); + Assert.False(m.IsVirtual); Assert.False(m.IsSealed); Assert.False(m.IsOverride); - Assert.True(m.HasSpecialName); + Assert.False(m.HasSpecialName); Assert.False(m.HasRuntimeSpecialName); - Assert.Equal(Accessibility.Public, m.DeclaredAccessibility); + Assert.Equal(Accessibility.Private, m.DeclaredAccessibility); + Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString()); } } [Theory] [CombinatorialData] - public void CompoundAssignment_00220_VirtualIsOptionalInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00130_AbstractCanBeImplementedImplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) { - string checkedForm = null; + var source = @" +interface I1 +{ + void operator " + op + @"(int x); +} - if (CompoundAssignmentOperatorHasCheckedForm(op)) - { - checkedForm = @" - void operator checked" + op + @"(int x) {} +" + typeKeyword + @" C3 : I1 +{ + public void operator " + op + @"(int x) {} +} "; - } + bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); - var source = @" -interface C1 + if (hasCheckedForm) + { + source += @" +interface I2 { - void operator" + op + @"(int x) {} -" + checkedForm + @" + 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, targetFramework: TargetFramework.Net90); CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); void validate(ModuleSymbol m) { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - if (checkedForm is not null) + validateOp(m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType(). + Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: false)).Single()); + if (hasCheckedForm) { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); + validateOp(m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType(). + Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: true)).Single()); } } @@ -7173,28 +7542,34 @@ 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.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); - Assert.Equal(Accessibility.Public, m.DeclaredAccessibility); } } [Theory] [CombinatorialData] - public void CompoundAssignment_00230_VirtualCanBeImplementedInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00140_AbstractAllowedOnExplicitImplementationInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" interface I1 { - virtual void operator " + op + @"(int x) {} + void operator " + op + @"(int x) {} } interface I3 : I1 { - void I1.operator " + op + @"(int x) {} + abstract void I1.operator " + op + @"(int x); } "; bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); @@ -7204,24 +7579,14 @@ interface I3 : I1 source += @" interface I2 { - void operator checked " + op + @"(int x); + void operator checked " + op + @"(int x) {} sealed void operator " + op + @"(int x) {} } interface I4 : I2 { - void I2.operator checked " + op + @"(int x) {} + abstract void I2.operator checked " + op + @"(int x); } - -class C : I3, I4 -{} -"; - } - else - { - source += @" -class C : I3 -{} "; } @@ -7245,265 +7610,202 @@ static void validateOp(MethodSymbol m, string implements) { Assert.Equal(MethodKind.ExplicitInterfaceImplementation, m.MethodKind); Assert.False(m.IsStatic); - Assert.False(m.IsAbstract); + Assert.True(m.IsAbstract); Assert.False(m.IsVirtual); - Assert.False(m.IsSealed); + 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_00240_VirtualCanBeImplementedExplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) + public void CompoundAssignment_00150_AbstractCannotHaveBody([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] 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); + string checkedForm = null; - if (hasCheckedForm) + if (CompoundAssignmentOperatorHasCheckedForm(op)) { - source += @" -interface I2 -{ - void operator checked " + op + @"(int x); - sealed void operator " + op + @"(int x) {} -} + checkedForm = @" + public abstract void operator checked" + op + @"(int x) {} +"; + } -" + typeKeyword + @" C4 : I2 + var source = +typeKeyword + @" C1 { - void I2.operator checked " + op + @"(int x) {} + public abstract void operator" + op + @"(int x) {} +" + checkedForm + @" } "; - } - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); - - void validate(ModuleSymbol m) - { - validateOp( - m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType().Where(m => !m.IsConstructor()).Single(), - "void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)"); - if (hasCheckedForm) - { - validateOp( - m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType().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()); - } + 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_00250_VirtualCanBeImplementedImplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) + public void CompoundAssignment_00160_AbstractExplicitImplementationCannotHaveBody([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { 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) {} + void operator" + op + @"(int x); } -" + typeKeyword + @" C4 : I2 +interface I2 : I1 { - public void operator checked " + op + @"(int x) {} - public void operator " + op + @"(int x) {} + abstract void I1.operator" + op + @"(int x) {} } "; - } - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); - - void validate(ModuleSymbol m) - { - validateOp(m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType(). - Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: false)).Single()); - if (hasCheckedForm) - { - validateOp(m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType(). - 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); - } + 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_00260_VirtualMustHaveBody([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword) + public void CompoundAssignment_00161_AbstractExplicitImplementationCannotHaveBody_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +interface I1 { - public virtual void operator" + op + @"(int x); + sealed void operator" + op + @"(int x) {} + void operator checked" + op + @"(int x); } -"; - var comp = CreateCompilation(source, 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 +interface I2 : I1 { - public void operator" + op + @"(int x) {} - public virtual void operator checked" + op + @"(int x); + abstract void I1.operator checked" + op + @"(int x) {} } "; var comp = CreateCompilation(source, 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) + // (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_00270_VirtualNotAllowedInSealedClass([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00170_AbstractNotAllowedInNonAbstractClass([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("sealed", "")] string typeModifier) { var source = @" -sealed class C1 +" + typeModifier + @" class C1 { - public virtual void operator" + op + @"(int x) {} + public abstract void operator" + op + @"(int x); } "; - var comp = CreateCompilation(source); - 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) - ); - } + bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); - [Theory] - [CombinatorialData] - public void CompoundAssignment_00271_VirtualNotAllowedInSealedClass_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) - { - var source = @" -sealed class C2 + if (hasCheckedForm) + { + source += +typeModifier + @" class C2 { -#line 9 - public virtual void operator checked " + op + @"(int x) {} + public abstract void operator checked " + op + @"(int x); public void operator " + op + @"(int x) {} } "; + } + var comp = CreateCompilation(source); 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) + 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_00290_VirtualNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00190_AbstractNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" struct C1 { - public virtual void operator" + op + @"(int x) {} + public abstract void operator" + op + @"(int x) {} } "; var comp = CreateCompilation(source); 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) + // (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_00291_VirtualNotAllowedInStruct_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00191_AbstractNotAllowedInStruct_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" struct C2 { -#line 9 - public virtual void operator checked " + op + @"(int x) {} + public abstract void operator checked " + op + @"(int x) {} public void operator " + op + @"(int x) {} } "; var comp = CreateCompilation(source); 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) + // (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_00300_VirtualNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00200_AbstractNotAllowedOnExplicitImplementationInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "struct")] string typeKeyword) { var source = @" interface I1 @@ -7513,21 +7815,20 @@ interface I1 " + typeKeyword + @" C3 : I1 { -#line 15 - virtual void I1.operator " + op + @"(int x) {} + abstract void I1.operator " + op + @"(int x) {} } "; var comp = CreateCompilation(source, 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) + // (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_00301_VirtualNotAllowedOnExplicitImplementation_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00201_AbstractNotAllowedOnExplicitImplementationInClassAndStruct_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "struct")] string typeKeyword) { var source = @" interface I2 @@ -7538,434 +7839,431 @@ interface I2 " + typeKeyword + @" C4 : I2 { -#line 20 - virtual void I2.operator checked " + op + @"(int x) {} + abstract void I2.operator checked " + op + @"(int x) {} } "; var comp = CreateCompilation(source, 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) + // (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_00320_VirtualAbstractNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) + 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 abstract void operator" + op + @"(int x) {} + public virtual void operator" + op + @"(int x) {} +" + checkedForm + @" } "; var comp = CreateCompilation(source, 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) - ); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + + void validate(ModuleSymbol m) + { + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + if (checkedForm is not null) + { + validateOp(m.GlobalNamespace.GetMember("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_00321_VirtualAbstractNotAllowed_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) + public void CompoundAssignment_00220_VirtualIsOptionalInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { - var source = -typeKeyword + @" C1 + string checkedForm = null; + + if (CompoundAssignmentOperatorHasCheckedForm(op)) + { + checkedForm = @" + void operator checked" + op + @"(int x) {} +"; + } + + var source = @" +interface C1 { - public void operator" + op + @"(int x) {} -#line 4 - public virtual abstract void operator checked" + op + @"(int x) {} + void operator" + op + @"(int x) {} +" + checkedForm + @" } "; var comp = CreateCompilation(source, 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) - ); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + + void validate(ModuleSymbol m) + { + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + if (checkedForm is not null) + { + validateOp(m.GlobalNamespace.GetMember("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_00330_OverrideAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool abstractInBase) + public void CompoundAssignment_00230_VirtualCanBeImplementedInInterface([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { 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 +interface I1 { - public override void operator" + op + @"(int x) {} - public override void operator checked" + op + @"(int x) {} + virtual void operator " + op + @"(int x) {} } -class C3 : C2 +interface I3 : I1 { - public override void operator" + op + @"(int x) {} - public override void operator checked" + op + @"(int x) {} + void I1.operator " + op + @"(int x) {} } "; - var comp = CreateCompilation(source); - CompileAndVerify(comp, symbolValidator: validate1, sourceSymbolValidator: validate1).VerifyDiagnostics(); + bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); - var source2 = @" -abstract class C1 + if (hasCheckedForm) + { + source += @" +interface I2 { - public virtual void operator" + op + @"(int x) {} - public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" + void operator checked " + op + @"(int x); + sealed void operator " + op + @"(int x) {} } -class C2 : C1 +interface I4 : I2 { - public override void operator checked" + op + @"(int x) {} + void I2.operator checked " + op + @"(int x) {} } -"; - var comp2 = CreateCompilation(source2); - - CompileAndVerify(comp2, symbolValidator: validate2, sourceSymbolValidator: validate2).VerifyDiagnostics(); - void validate1(ModuleSymbol m) +class C : I3, I4 +{} +"; + } + else { - validateOp( - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), - m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp( - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true)), - m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); - - validateOp( - m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false)), - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp( - m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: true)), - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true))); + source += @" +class C : I3 +{} +"; } - void validate2(ModuleSymbol m) + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + + void validate(ModuleSymbol m) { validateOp( - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true)), - m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); + m.GlobalNamespace.GetTypeMember("I3").GetMembers().OfType().Single(), + "void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)"); + if (hasCheckedForm) + { + validateOp( + m.GlobalNamespace.GetTypeMember("I4").GetMembers().OfType().Single(), + "void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)"); + } } - static void validateOp(MethodSymbol m, MethodSymbol overridden) + static void validateOp(MethodSymbol m, string implements) { - Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, 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.IsOverride); + Assert.False(m.HasSpecialName); Assert.False(m.HasRuntimeSpecialName); - Assert.Equal(Accessibility.Public, m.DeclaredAccessibility); - Assert.Same(overridden, m.OverriddenMethod); + Assert.Equal(Accessibility.Private, m.DeclaredAccessibility); + Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString()); } } [Theory] [CombinatorialData] - public void CompoundAssignment_00331_OverrideAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool abstractInBase) + public void CompoundAssignment_00240_VirtualCanBeImplementedExplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) { var source = @" -abstract class C1 +interface I1 { - public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" + virtual void operator " + op + @"(int x) {} } -class C2 : C1 +" + typeKeyword + @" C3 : I1 { - public override void operator" + op + @"(int x) {} + void I1.operator " + op + @"(int x) {} } +"; + bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); -class C3 : C2 + if (hasCheckedForm) + { + source += @" +interface I2 { - public override void operator" + op + @"(int x) {} + 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); - CompileAndVerify(comp, symbolValidator: validate1, sourceSymbolValidator: validate1).VerifyDiagnostics(); + } - void validate1(ModuleSymbol m) - { - validateOp( - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), - m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + void validate(ModuleSymbol m) + { validateOp( - m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false)), - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); + m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType().Where(m => !m.IsConstructor()).Single(), + "void I1." + CompoundAssignmentOperatorName(op, isChecked: false) + "(System.Int32 x)"); + if (hasCheckedForm) + { + validateOp( + m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType().Where(m => !m.IsConstructor()).Single(), + "void I2." + CompoundAssignmentOperatorName(op, isChecked: true) + "(System.Int32 x)"); + } } - static void validateOp(MethodSymbol m, MethodSymbol overridden) + static void validateOp(MethodSymbol m, string implements) { - Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); + Assert.Equal(MethodKind.ExplicitInterfaceImplementation, 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.IsOverride); + Assert.False(m.HasSpecialName); Assert.False(m.HasRuntimeSpecialName); - Assert.Equal(Accessibility.Public, m.DeclaredAccessibility); - Assert.Same(overridden, m.OverriddenMethod); + Assert.Equal(Accessibility.Private, m.DeclaredAccessibility); + Assert.Equal(implements, m.ExplicitInterfaceImplementations.Single().ToTestDisplayString()); } } [Theory] [CombinatorialData] - public void CompoundAssignment_00340_AbstractOverrideAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool abstractInBase) + public void CompoundAssignment_00250_VirtualCanBeImplementedImplicitlyInClassAndStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) { var source = @" -abstract class C1 +interface I1 { - public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" - public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" + virtual void operator " + op + @"(int x) {} } -abstract class C2 : C1 +" + typeKeyword + @" C3 : I1 { - public abstract override void operator" + op + @"(int x); - public abstract override void operator checked" + op + @"(int x); + public void operator " + op + @"(int x) {} } +"; + bool hasCheckedForm = CompoundAssignmentOperatorHasCheckedForm(op); -class C3 : C2 + if (hasCheckedForm) + { + source += @" +interface I2 { - public override void operator" + op + @"(int x) {} - public override void operator checked" + op + @"(int x) {} + 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); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + } + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); void validate(ModuleSymbol m) { - validateOp( - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), - m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp( - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true)), - m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); - - validateOp( - m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false)), - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp( - m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: true)), - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true))); + validateOp(m.GlobalNamespace.GetTypeMember("C3").GetMembers().OfType(). + Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: false)).Single()); + if (hasCheckedForm) + { + validateOp(m.GlobalNamespace.GetTypeMember("C4").GetMembers().OfType(). + Where(m => m.Name == CompoundAssignmentOperatorName(op, isChecked: true)).Single()); + } } - static void validateOp(MethodSymbol m, MethodSymbol overridden) + static void validateOp(MethodSymbol m) { Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); Assert.False(m.IsStatic); - Assert.Equal(m.ContainingType.Name == "C2", m.IsAbstract); + Assert.False(m.IsAbstract); Assert.False(m.IsVirtual); Assert.False(m.IsSealed); - Assert.True(m.IsOverride); + + if (m is PEMethodSymbol) + { + Assert.True(m.IsMetadataVirtual()); + Assert.True(m.IsMetadataFinal); + } + + Assert.False(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]); - 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) + public void CompoundAssignment_00260_VirtualMustHaveBody([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword) { - 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 + var source = +typeKeyword + @" C1 { - public override void operator" + op + @"(int x) {} + public virtual void operator" + op + @"(int x); } "; - var comp = CreateCompilation(source); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); - - void validate(ModuleSymbol m) - { - validateOp( - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), - m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - - validateOp( - m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false)), - m.GlobalNamespace.GetMember("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]); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); 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) + // (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_00350_OverrideAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00261_VirtualMustHaveBody_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword) { - var source = @" -struct S1 + var source = +typeKeyword + @" C1 { - public override void operator" + op + @"(int x) {} - public override void operator checked" + op + @"(int x) {} + public void operator" + op + @"(int x) {} + public virtual void operator checked" + op + @"(int x); } "; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - - validateOp(comp.GetMember("S1." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(comp.GetMember("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) + // (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) ); - - 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) + public void CompoundAssignment_00270_VirtualNotAllowedInSealedClass([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" -struct S1 +sealed class C1 { - public override void operator" + op + @"(int x) {} + public virtual void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - - validateOp(comp.GetMember("S1." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(comp.GetMember("S1." + CompoundAssignmentOperatorName(op, isChecked: true))); - + var comp = CreateCompilation(source); 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) + // (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) ); - - 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) + public void CompoundAssignment_00271_VirtualNotAllowedInSealedClass_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -interface I1 +sealed class C2 { - public override void operator" + op + @"(int x) {} +#line 9 + public virtual void operator checked " + op + @"(int x) {} + public void operator " + op + @"(int x) {} } +"; + var comp = CreateCompilation(source); + 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) + ); + } -interface I2 + [Theory] + [CombinatorialData] + public void CompoundAssignment_00290_VirtualNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + { + var source = @" +struct C1 { - public override void operator checked " + op + @"(int x) {} - public void operator " + op + @"(int x) {} + public virtual void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + var comp = CreateCompilation(source); 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) + // (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_00371_OverrideNotAllowedInInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00291_VirtualNotAllowedInStruct_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -interface I1 +struct C2 { - public override void operator" + op + @"(int x) {} +#line 9 + public virtual void operator checked " + op + @"(int x) {} + public void operator " + op + @"(int x) {} } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + var comp = CreateCompilation(source); 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,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_00380_OverrideNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00300_VirtualNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) { var source = @" interface I1 @@ -7973,233 +8271,196 @@ 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) {} +#line 15 + virtual void I1.operator " + op + @"(int x) {} } "; var comp = CreateCompilation(source, 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) + // (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_00381_OverrideNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00301_VirtualNotAllowedOnExplicitImplementation_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) { var source = @" -interface I1 +interface I2 { - void operator " + op + @"(int x); + void operator checked " + op + @"(int x); + sealed void operator " + op + @"(int x) {} } -" + typeKeyword + @" C3 : I1 +" + typeKeyword + @" C4 : I2 { -#line 15 - override void I1.operator " + op + @"(int x) {} +#line 20 + virtual void I2.operator checked " + op + @"(int x) {} } "; var comp = CreateCompilation(source, 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,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_00400_VirtualOverrideNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00320_VirtualAbstractNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) { - var source = @" -class C1 -{ - public virtual void operator" + op + @"(int x) {} - public virtual void operator checked" + op + @"(int x) {} -} - -class C2 : C1 + var source = +typeKeyword + @" C1 { - public virtual override void operator" + op + @"(int x) {} - public virtual override void operator checked" + op + @"(int x) {} + public virtual abstract void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); 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) + // (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_00401_VirtualOverrideNotAllowed([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00321_VirtualAbstractNotAllowed_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) { - var source = @" -class C1 -{ - public virtual void operator" + op + @"(int x) {} -} - -class C2 : C1 + var source = +typeKeyword + @" C1 { -#line 10 - public virtual override void operator" + op + @"(int x) {} + public void operator" + op + @"(int x) {} +#line 4 + public virtual abstract void operator checked" + op + @"(int x) {} } "; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); 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) + // (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_00410_SealedAllowedInInterface([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00330_OverrideAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool abstractInBase) { var source = @" -interface C1 +abstract class C1 { - sealed void operator" + op + @"(int x) {} - sealed void operator checked" + op + @"(int x) {} + public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" + public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" } -"; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); - void validate(ModuleSymbol m) - { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); - } +class C2 : C1 +{ + public override void operator" + op + @"(int x) {} + public override void operator checked" + op + @"(int x) {} +} - 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); - } +class C3 : C2 +{ + public override void operator" + op + @"(int x) {} + public override void operator checked" + op + @"(int x) {} +} +"; + var comp = CreateCompilation(source); + CompileAndVerify(comp, symbolValidator: validate1, sourceSymbolValidator: validate1).VerifyDiagnostics(); var source2 = @" -class C3 : C1 +abstract class C1 { - void C1.operator" + op + @"(int x) {} - void C1.operator checked" + op + @"(int x) {} + public virtual void operator" + op + @"(int x) {} + public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" } -"; - 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 +class C2 : C1 { - sealed void operator" + op + @"(int x) {} + public override void operator checked" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + var comp2 = CreateCompilation(source2); - void validate(ModuleSymbol m) + CompileAndVerify(comp2, symbolValidator: validate2, sourceSymbolValidator: validate2).VerifyDiagnostics(); + + void validate1(ModuleSymbol m) { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp( + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), + m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp( + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true)), + m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); + + validateOp( + m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false)), + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp( + m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: true)), + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true))); } - static void validateOp(MethodSymbol m) + void validate2(ModuleSymbol m) + { + validateOp( + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true)), + m.GlobalNamespace.GetMember("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.False(m.IsOverride); + 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 : 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) + 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 ? ";" : "{}") + @" - public " + (abstractInBase ? "abstract" : "virtual") + @" void operator checked" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" } -abstract class C2 : C1 +class C2 : C1 { - public sealed override void operator" + op + @"(int x) {} - public sealed override void operator checked" + op + @"(int x) {} + public override void operator" + op + @"(int x) {} +} + +class C3 : C2 +{ + public override void operator" + op + @"(int x) {} } "; var comp = CreateCompilation(source); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + CompileAndVerify(comp, symbolValidator: validate1, sourceSymbolValidator: validate1).VerifyDiagnostics(); - void validate(ModuleSymbol m) + void validate1(ModuleSymbol m) { validateOp( m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp( - m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true)), - m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); + m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false)), + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); } static void validateOp(MethodSymbol m, MethodSymbol overridden) @@ -8208,45 +8469,36 @@ static void validateOp(MethodSymbol m, MethodSymbol overridden) Assert.False(m.IsStatic); Assert.False(m.IsAbstract); Assert.False(m.IsVirtual); - Assert.True(m.IsSealed); + 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); } - - var source2 = @" -class C3 : C2 -{ - public override void operator" + op + @"(int x) {} - public override void operator checked" + op + @"(int x) {} -} -"; - comp = CreateCompilation([source2, source]); - 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) + 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 sealed override void operator" + op + @"(int x) {} + 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); @@ -8257,15 +8509,25 @@ void validate(ModuleSymbol m) validateOp( m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - } + validateOp( + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true)), + m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); - static void validateOp(MethodSymbol m, MethodSymbol overridden) - { + validateOp( + m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false)), + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp( + m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: true)), + m.GlobalNamespace.GetMember("C2." + 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.Equal(m.ContainingType.Name == "C2", m.IsAbstract); Assert.False(m.IsVirtual); - Assert.True(m.IsSealed); + Assert.False(m.IsSealed); Assert.True(m.IsOverride); Assert.True(m.HasSpecialName); Assert.False(m.HasRuntimeSpecialName); @@ -8273,1131 +8535,1070 @@ static void validateOp(MethodSymbol m, MethodSymbol overridden) Assert.Same(overridden, m.OverriddenMethod); } - var source2 = @" -class C3 : C2 -{ - public override void operator" + op + @"(int x) {} -} -"; - comp = CreateCompilation([source2, source]); + comp = CreateCompilation(["class C4 : C2 {}", source]); 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) + // (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_00430_SealedNotAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00341_AbstractOverrideAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool abstractInBase) { var source = @" -class C1 +abstract class C1 { - public sealed void operator" + op + @"(int x) {} + public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" } -class C2 +abstract class C2 : C1 { - public sealed void operator checked " + op + @"(int x) {} - public void operator " + op + @"(int x) {} + public abstract override void operator" + op + @"(int x); } -"; - var comp = CreateCompilation(source); - 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 +class C3 : C2 { - public sealed void operator" + op + @"(int x) {} + public override void operator" + op + @"(int x) {} } "; var comp = CreateCompilation(source); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + + void validate(ModuleSymbol m) + { + validateOp( + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), + m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + + validateOp( + m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false)), + m.GlobalNamespace.GetMember("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]); 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) + // (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_00440_SealedNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00350_OverrideAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -struct C1 -{ - public sealed void operator" + op + @"(int x) {} -} - -struct C2 +struct S1 { - public sealed void operator checked " + op + @"(int x) {} - public void operator " + op + @"(int x) {} + public override void operator" + op + @"(int x) {} + public override void operator checked" + op + @"(int x) {} } "; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + + validateOp(comp.GetMember("S1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(comp.GetMember("S1." + CompoundAssignmentOperatorName(op, isChecked: true))); + 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) + // (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_00441_SealedNotAllowedInStruct([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00351_OverrideAllowedInStruct([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" -struct C1 +struct S1 { - public sealed void operator" + op + @"(int x) {} + public override void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + + validateOp(comp.GetMember("S1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(comp.GetMember("S1." + CompoundAssignmentOperatorName(op, isChecked: true))); + 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) + // (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_00450_SealedOverrideNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00370_OverrideNotAllowedInInterface([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -struct C1 +interface I1 { - public sealed override void operator" + op + @"(int x) {} + public override void operator" + op + @"(int x) {} } -struct C2 +interface I2 { - public sealed override void operator checked " + op + @"(int x) {} + public override void operator checked " + op + @"(int x) {} public void operator " + op + @"(int x) {} } "; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); 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) + // (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_00451_SealedOverrideNotAllowedInStruct([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00371_OverrideNotAllowedInInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" -struct C1 +interface I1 { - public sealed override void operator" + op + @"(int x) {} + public override void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); 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) + // (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_00460_SealedAbstractOverrideNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00380_OverrideNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) { var source = @" -struct C1 +interface I1 { - public sealed abstract override void operator" + op + @"(int x) {} + void operator " + op + @"(int x); } -struct C2 +interface I2 { - public sealed abstract override void operator checked " + op + @"(int x) {} - public void operator " + op + @"(int x) {} + 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); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); 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) + // (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_00461_SealedAbstractOverrideNotAllowedInStruct([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00381_OverrideNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) { var source = @" -struct C1 +interface I1 { - public sealed abstract override void operator" + op + @"(int x) {} + void operator " + op + @"(int x); +} + +" + typeKeyword + @" C3 : I1 +{ +#line 15 + override void I1.operator " + op + @"(int x) {} } "; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); 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) + // (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_00470_SealedNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00400_VirtualOverrideNotAllowed([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) {} -} - -" + typeKeyword + @" C3 : I1 +class C1 { - sealed void I1.operator " + op + @"(int x) {} + public virtual void operator" + op + @"(int x) {} + public virtual void operator checked" + op + @"(int x) {} } -" + typeKeyword + @" C4 : I2 +class C2 : C1 { - sealed void I2.operator checked " + op + @"(int x) {} + public virtual override void operator" + op + @"(int x) {} + public virtual override void operator checked" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + var comp = CreateCompilation(source); 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) + // (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_00471_SealedNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00401_VirtualOverrideNotAllowed([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" -interface I1 +class C1 { - void operator " + op + @"(int x); + public virtual void operator" + op + @"(int x) {} } -" + typeKeyword + @" C3 : I1 +class C2 : C1 { -#line 15 - sealed void I1.operator " + op + @"(int x) {} +#line 10 + public virtual override void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + var comp = CreateCompilation(source); 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) + // (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_00490_SealedAbstractNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) + public void CompoundAssignment_00410_SealedAllowedInInterface([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +interface C1 { - public sealed abstract void operator" + op + @"(int x); - public sealed abstract void operator checked" + op + @"(int x); + sealed void operator" + op + @"(int x) {} + sealed void operator checked" + op + @"(int x) {} } "; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - if (typeKeyword == "interface") + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + + void validate(ModuleSymbol m) { - 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) - ); + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); } - else + + static void validateOp(MethodSymbol m) { - 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) - ); + 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_00491_SealedAbstractNotAllowed([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) + public void CompoundAssignment_00411_SealedAllowedInInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +interface C1 { - public sealed abstract void operator" + op + @"(int x); + sealed void operator" + op + @"(int x) {} } "; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); - if (typeKeyword == "interface") + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(); + + void validate(ModuleSymbol m) { - 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) - ); + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); } - else + + static void validateOp(MethodSymbol m) { - 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) - ); + 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_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 + var source2 = @" +class C3 : C1 { - public sealed abstract override void operator" + op + @"(int x); - public sealed abstract override void operator checked" + op + @"(int x); + void C1.operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source); + comp = CreateCompilation([source2, source], targetFramework: TargetFramework.Net90); 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) + // (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_00501_SealedAbstractOverrideNotAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool abstractInBase) + 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 abstract override void operator" + op + @"(int x); + public sealed override void operator" + op + @"(int x) {} + public sealed override void operator checked" + op + @"(int x) {} } "; var comp = CreateCompilation(source); - 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) - ); - } + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); - [Theory] - [CombinatorialData] - public void CompoundAssignment_00510_SealedAbstractNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op) - { - var source = @" -interface I1 -{ - void operator " + op + @"(int x); -} + void validate(ModuleSymbol m) + { + validateOp( + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), + m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp( + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true)), + m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); + } -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); -} + 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); + } -interface I4 : I2 + var source2 = @" +class C3 : C2 { - sealed abstract void I2.operator checked " + op + @"(int x); + public override void operator" + op + @"(int x) {} + public override void operator checked" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + comp = CreateCompilation([source2, source]); 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) + // (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_00511_SealedAbstractNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00421_SealedOverrideAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool abstractInBase) { var source = @" -interface I1 +abstract class C1 { - void operator " + op + @"(int x); + public " + (abstractInBase ? "abstract" : "virtual") + @" void operator" + op + @"(int x) " + (abstractInBase ? ";" : "{}") + @" } -interface I3 : I1 +abstract class C2 : C1 { -#line 15 - sealed abstract void I1.operator " + op + @"(int x); + public sealed override void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + var comp = CreateCompilation(source); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + + void validate(ModuleSymbol m) + { + validateOp( + m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false)), + m.GlobalNamespace.GetMember("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]); 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) + // (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_00530_SealedVirtualNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword) + public void CompoundAssignment_00430_SealedNotAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +class C1 { - public virtual sealed void operator" + op + @"(int x) {} - public virtual sealed void operator checked" + op + @"(int x) {} + 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, 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) - ); - } + var comp = CreateCompilation(source); + 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_00531_SealedVirtualNotAllowed([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword) + public void CompoundAssignment_00431_SealedNotAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +class C1 { - public virtual sealed void operator" + op + @"(int x) {} + public sealed void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, 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) - ); - } + var comp = CreateCompilation(source); + 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_00540_NewAllowedInInterface_WRN_NewRequired([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract", "virtual", "sealed")] string baseModifier) + public void CompoundAssignment_00440_SealedNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { 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 +struct C1 { - public void operator" + op + @"(int x) {} - public void operator checked" + op + @"(int x) {} + public sealed void operator" + op + @"(int x) {} } -interface C3 : C1 +struct C2 { - public new void operator" + op + @"(int x) {} - public new void operator checked" + op + @"(int x) {} + public sealed void operator checked " + op + @"(int x) {} + public void operator " + op + @"(int x) {} } "; - var comp = CreateCompilation(source, 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) + var comp = CreateCompilation(source); + 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) ); - - void validate(ModuleSymbol m) - { - validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true))); - validateOp(m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("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) + public void CompoundAssignment_00441_SealedNotAllowedInStruct([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" -interface C1 +struct C1 { - public " + baseModifier + @" void operator" + op + @"(int x) " + (baseModifier == "abstract" ? ";" : "{}") + @" + public sealed void operator" + op + @"(int x) {} } +"; + var comp = CreateCompilation(source); + 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) + ); + } -interface C2 : C1 + [Theory] + [CombinatorialData] + public void CompoundAssignment_00450_SealedOverrideNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + { + var source = @" +struct C1 { -#line 10 - public void operator" + op + @"(int x) {} + public sealed override void operator" + op + @"(int x) {} } -interface C3 : C1 +struct C2 { - public new void operator" + op + @"(int x) {} + public sealed override void operator checked " + op + @"(int x) {} + public void operator " + op + @"(int x) {} } "; - var comp = CreateCompilation(source, 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("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("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); - } + var comp = CreateCompilation(source); + 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_00550_NewAllowedInClass_WRN_NewRequired([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + public void CompoundAssignment_00451_SealedOverrideNotAllowedInStruct([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 +struct C1 { - public new void operator" + op + @"(int x) {} - public new void operator checked" + op + @"(int x) {} + public sealed override void operator" + op + @"(int x) {} } "; var comp = CreateCompilation(source); - 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) + 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) ); - - void validate(ModuleSymbol m) - { - validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true))); - validateOp(m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("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) + public void CompoundAssignment_00460_SealedAbstractOverrideNotAllowedInStruct([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -class C1 -{ - public void operator" + op + @"(int x) {} -} - -class C2 : C1 +struct C1 { -#line 10 - public void operator" + op + @"(int x) {} + public sealed abstract override void operator" + op + @"(int x) {} } -class C3 : C1 +struct C2 { - public new void operator" + op + @"(int x) {} + public sealed abstract override void operator checked " + op + @"(int x) {} + public void operator " + op + @"(int x) {} } "; var comp = CreateCompilation(source); - 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) + 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) ); - - void validate(ModuleSymbol m) - { - validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("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) + public void CompoundAssignment_00461_SealedAbstractOverrideNotAllowedInStruct([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 +struct C1 { - public new void operator" + op + @"(int x) {} - public new void operator checked" + op + @"(int x) {} + public sealed abstract override void operator" + op + @"(int x) {} } "; var comp = CreateCompilation(source); - 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) + 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) ); - - void validate(ModuleSymbol m) - { - validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true))); - validateOp(m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("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) + public void CompoundAssignment_00470_SealedNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) { var source = @" -class C1 +interface I1 { - public virtual void operator" + op + @"(int x) {} + void operator " + op + @"(int x); } -class C2 : C1 +interface I2 { -#line 10 - public void operator" + op + @"(int x) {} + void operator checked " + op + @"(int x); + sealed void operator " + op + @"(int x) {} } -class C3 : C1 +" + typeKeyword + @" C3 : I1 { - public new void operator" + op + @"(int x) {} + sealed void I1.operator " + op + @"(int x) {} +} + +" + typeKeyword + @" C4 : I2 +{ + sealed void I2.operator checked " + op + @"(int x) {} } "; - var comp = CreateCompilation(source); - 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) + var comp = CreateCompilation(source, 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) ); - - void validate(ModuleSymbol m) - { - validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("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) + public void CompoundAssignment_00471_SealedNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) { var source = @" -abstract class C1 +interface I1 { - 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); - 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) {} + void operator " + op + @"(int x); } -abstract class C3 : C1 +" + typeKeyword + @" C3 : I1 { -#line 16 - public new void operator" + op + @"(int x) {} +#line 15 + sealed void I1.operator " + op + @"(int x) {} } "; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); 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) + // (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_00580_NewAllowed_WRN_NewNotRequired([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00490_SealedAbstractNotAllowed([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) { var source = typeKeyword + @" C1 { - public new void operator" + op + @"(int x) {} - public new void operator checked" + op + @"(int x) {} + public sealed abstract void operator" + op + @"(int x); + public sealed abstract void operator checked" + op + @"(int x); } "; var comp = CreateCompilation(source, 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) + if (typeKeyword == "interface") { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); + 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) + ); } - - void validateOp(MethodSymbol m) + else { - 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); + 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_00581_NewAllowed_WRN_NewNotRequired([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00491_SealedAbstractNotAllowed([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) { var source = typeKeyword + @" C1 { - public new void operator" + op + @"(int x) {} + public sealed abstract void operator" + op + @"(int x); } "; var comp = CreateCompilation(source, 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) + if (typeKeyword == "interface") { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + 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) + ); } - - void validateOp(MethodSymbol m) + else { - 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); + 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_00590_NewAbstractAllowedInClassAndInterface([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("abstract class", "interface")] string typeKeyword) + public void CompoundAssignment_00500_SealedAbstractOverrideNotAllowedInClass([CombinatorialValues("+=", "-=", "*=", "/=")] string op, bool abstractInBase) { - var source = -typeKeyword + @" C1 + var source = @" +abstract class C1 { - public new abstract void operator" + op + @"(int x); - public new abstract void operator checked" + op + @"(int x); + 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, 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) + var comp = CreateCompilation(source); + 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) ); - - void validate(ModuleSymbol m) - { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("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) + public void CompoundAssignment_00501_SealedAbstractOverrideNotAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, bool abstractInBase) { - var source = -typeKeyword + @" C1 + var source = @" +abstract class C1 { - public new abstract void operator" + op + @"(int x); + 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, 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) + var comp = CreateCompilation(source); + 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) ); - - void validate(ModuleSymbol m) - { - validateOp(m.GlobalNamespace.GetMember("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) + public void CompoundAssignment_00510_SealedAbstractNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { - var source = -typeKeyword + @" C1 + var source = @" +interface I1 { - public new virtual void operator" + op + @"(int x) {} - public new virtual void operator checked" + op + @"(int x) {} + void operator " + op + @"(int x); } -"; - var comp = CreateCompilation(source, 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) + +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, 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) ); + } - void validate(ModuleSymbol m) + [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, 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, targetFramework: TargetFramework.Net90); + if (typeKeyword == "interface") { - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); + 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) + ); } - - static void validateOp(MethodSymbol m) + else { - 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); + 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_00601_NewVirtualAllowedInClassAndInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword) + public void CompoundAssignment_00531_SealedVirtualNotAllowed([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "interface")] string typeKeyword) { var source = typeKeyword + @" C1 { - public new virtual void operator" + op + @"(int x) {} + public virtual sealed void operator" + op + @"(int x) {} +} +"; + var comp = CreateCompilation(source, 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, 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) + // (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("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true))); + validateOp(m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: true))); } - static void validateOp(MethodSymbol m) + void validateOp(MethodSymbol m) { Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); Assert.False(m.IsStatic); @@ -9407,74 +9608,98 @@ static void validateOp(MethodSymbol m) 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) + public void CompoundAssignment_00541_NewAllowedInInterface_WRN_NewRequired([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("abstract", "virtual", "sealed")] string baseModifier) { var source = @" interface C1 { - sealed new void operator" + op + @"(int x) {} - sealed new void operator checked" + op + @"(int x) {} + 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, 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) + // (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("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); - validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: true))); + validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false))); } - static void validateOp(MethodSymbol m) + void validateOp(MethodSymbol m) { Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); Assert.False(m.IsStatic); Assert.False(m.IsAbstract); - Assert.False(m.IsVirtual); + 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_00611_NewSealedAllowedInInterface([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + public void CompoundAssignment_00550_NewAllowedInClass_WRN_NewRequired([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -interface C1 +class C1 { - sealed new void operator" + op + @"(int x) {} + 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, 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) + var comp = CreateCompilation(source); + 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("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true))); + validateOp(m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: true))); } - static void validateOp(MethodSymbol m) + void validateOp(MethodSymbol m) { Assert.Equal(MethodKind.UserDefinedOperator, m.MethodKind); Assert.False(m.IsStatic); @@ -9484,221 +9709,756 @@ static void validateOp(MethodSymbol m) 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) + public void CompoundAssignment_00551_NewAllowedInClass_WRN_NewRequired([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" class C1 { - public virtual void operator" + op + @"(int x) {} + public void operator" + op + @"(int x) {} } -class C2 +class C2 : C1 { - public virtual void operator checked " + op + @"(int x) {} - public void operator " + op + @"(int x) {} +#line 10 + 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) {} + public new void operator" + op + @"(int x) {} } "; var comp = CreateCompilation(source); - 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) + 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("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("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_00621_NewOverrideNotAllowedInClass([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + 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 C3 : C1 +class C2 : C1 { -#line 15 - public new override void operator" + op + @"(int x) {} + public void operator" + op + @"(int x) {} + public void operator checked" + op + @"(int x) {} } -"; - var comp = CreateCompilation(source); - 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 +class C3 : C1 { - public new override void operator" + op + @"(int x) {} + public new void operator" + op + @"(int x) {} + public new void operator checked" + op + @"(int x) {} } "; var comp = CreateCompilation(source); - 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) + 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("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("C2." + CompoundAssignmentOperatorName(op, isChecked: true))); + validateOp(m.GlobalNamespace.GetMember("C3." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("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_00650_NewNotAllowedOnExplicitImplementation([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00561_NewAllowedInClass_WRN_NewOrOverrideExpected([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" -interface I1 -{ - void operator " + op + @"(int x); -} - -interface I2 +class C1 { - void operator checked " + op + @"(int x); - sealed void operator " + op + @"(int x) {} + public virtual void operator" + op + @"(int x) {} } -" + typeKeyword + @" C3 : I1 +class C2 : C1 { - new void I1.operator " + op + @"(int x) {} +#line 10 + public void operator" + op + @"(int x) {} } -" + typeKeyword + @" C4 : I2 +class C3 : C1 { - new void I2.operator checked " + op + @"(int x) {} + public new void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, 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) + var comp = CreateCompilation(source); + 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("C2." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("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_00651_NewNotAllowedOnExplicitImplementation([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) + public void CompoundAssignment_00570_NewAllowedInClass_ERR_HidingAbstractMethod([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -interface I1 +abstract class C1 { - void operator " + op + @"(int x); + public abstract void operator" + op + @"(int x); + public abstract void operator checked" + op + @"(int x); } -" + typeKeyword + @" C3 : I1 +abstract class C2 : C1 { -#line 15 - new void I1.operator " + op + @"(int x) {} + 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, targetFramework: TargetFramework.Net90); + var comp = CreateCompilation(source); 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) + // (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_00670_ExplicitImplementationStaticVsInstanceMismatch([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) + public void CompoundAssignment_00571_NewAllowedInClass_ERR_HidingAbstractMethod([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) { var source = @" -interface I1 -{ - void operator " + op + @"(int x); -} - -interface I2 +abstract class C1 { - void operator checked " + op + @"(int x); - sealed void operator " + op + @"(int x) {} + public abstract void operator" + op + @"(int x); } -" + typeKeyword + @" C3 - : I1 +abstract class C2 : C1 { - static void I1.operator " + op + @"(int x) {} +#line 10 + public void operator" + op + @"(int x) {} } -" + typeKeyword + @" C4 - : I2 +abstract class C3 : C1 { - static void I2.operator checked " + op + @"(int x) {} +#line 16 + public new void operator" + op + @"(int x) {} } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + var comp = CreateCompilation(source); 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) + // (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_00671_ExplicitImplementationStaticVsInstanceMismatch([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op, [CombinatorialValues("class", "struct")] string typeKeyword) + public void CompoundAssignment_00580_NewAllowed_WRN_NewNotRequired([CombinatorialValues("+=", "-=", "*=", "/=")] string op, [CombinatorialValues("class", "struct", "interface")] string typeKeyword) { - var source = @" -interface I1 -{ - void operator " + op + @"(int x); -} - -" + typeKeyword + @" C3 - : I1 + var source = +typeKeyword + @" C1 { -#line 16 - static void I1.operator " + op + @"(int x) {} + public new void operator" + op + @"(int x) {} + public new void operator checked" + op + @"(int x) {} } "; var comp = CreateCompilation(source, 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) + 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) ); - } - [Theory] - [CombinatorialData] - public void CompoundAssignment_00680_ExplicitImplementationStaticVsInstanceMismatch([CombinatorialValues("+=", "-=", "*=", "/=")] string op) - { + void validate(ModuleSymbol m) + { + validateOp(m.GlobalNamespace.GetMember("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("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, 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("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, 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("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("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, 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("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, 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("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("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, 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("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, 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("C1." + CompoundAssignmentOperatorName(op, isChecked: false))); + validateOp(m.GlobalNamespace.GetMember("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, 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("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); + 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); + 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); + 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, 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, 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, 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, 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 { @@ -13657,179 +14417,472 @@ public void CompoundAssignment_00960_Implement_RegularVsOperatorMismatch(bool fr var source1 = @" public interface I1 { - void op_AdditionAssignment(int x); + void op_AdditionAssignment(int x); +} + +public interface I2 +{ + void operator +=(int x); +} +"; + var comp1 = CreateCompilation(source1); + + 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, references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()]); + comp2.VerifyDiagnostics( + // (7,25): error CS9504: '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 CS9504: '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, targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (10,19): error CS9504: '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 CS9504: '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, targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (10,19): error CS9504: '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 CS9504: '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_Increment]""); + } +} + +public class Program +{ + static void Main() + { + I1 x = new C3(); + x += 1; + } +} +"; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (15,23): error CS9504: '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_Increment]""); + } +} + +public class C3 : C2, I1 +{ + public virtual void operator +=(int x) + { + System.Console.Write(""[C3.operator]""); + } } -public interface I2 +public class Program { - void operator +=(int x); + static void Main() + { + I1 x = new C3(); + x.op_AdditionAssignment(1); + } } "; - var comp1 = CreateCompilation(source1); - var source2 = @" -public class C11 + var comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (15,23): error CS9504: '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 { - public void operator +=(int x) {} + .method public hidebysig newslot abstract virtual specialname + instance void op_AdditionAssignment (int32 x) cil managed + { + } } -public class C12 : C11, I1 +.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 + } } +"; -public class C21 + var source1 = +@" +public class C2 : C1, I1 { - public void op_AdditionAssignment(int x) {} } +"; + var compilation1 = CreateCompilationWithIL(source1, ilSource); -public class C22 : C21, I2 + compilation1.VerifyDiagnostics( + // (2,23): error CS9504: '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); - var comp2 = CreateCompilation(source2, references: [fromMetadata ? comp1.EmitToImageReference() : comp1.ToMetadataReference()]); - comp2.VerifyDiagnostics( - // (7,25): error CS9504: '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 CS9504: '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) - ); + 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_00970_Implement_RegularVsOperatorMismatch() + public void CompoundAssignment_01020_Implement_RegularVsOperatorMismatch() { - var source = @" -public interface I1 + /* + 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 { - public void operator +=(int x) + .method public hidebysig newslot abstract virtual + instance void op_AdditionAssignment (int32 x) cil managed { - System.Console.Write(""[I1.operator]""); - } + } } -public class C1 : I1 +.class public auto ansi beforefieldinit C1 + extends [mscorlib]System.Object + implements I1 { - public virtual void op_AdditionAssignment(int x) + .method public hidebysig newslot virtual specialname + instance void op_AdditionAssignment (int32 x) cil managed { - System.Console.Write(""[C1.op_AdditionAssignment]""); - } -} + .maxstack 8 -public class C2 -{ - public virtual void op_AdditionAssignment(int x) + 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 { - System.Console.Write(""[C2.op_AdditionAssignment]""); - } + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } } +"; -public class C3 : C2, I1 + var source1 = +@" +public class C2 : C1, I1 { } +"; + var compilation1 = CreateCompilationWithIL(source1, ilSource); -public class Program + compilation1.VerifyDiagnostics( + // (2,23): error CS9504: '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() { - I1 x = new C1(); - x += 1; - x = new C3(); - x += 1; - } + var c1 = new C1(); + c1 += 1; + I1 x = c1; + x.op_AdditionAssignment(1); + } } "; + var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe); - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe); - comp.VerifyDiagnostics( - // (10,19): error CS9504: '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 CS9504: '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) - ); + 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_00980_Implement_RegularVsOperatorMismatch() + public void CompoundAssignment_01030_Consumption_Implementation() { var source = @" public interface I1 { - public void op_AdditionAssignment(int x) - { - System.Console.Write(""[I1.operator]""); - } + public void operator +=(int x); } public class C1 : I1 { - public virtual void operator +=(int x) + public void operator +=(int x) { System.Console.Write(""[C1.operator]""); } } -public class C2 +public class C2 : I1 { - public virtual void operator +=(int x) + void I1.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); + x += 1; + x = new C2(); + x += 1; } } "; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe); - comp.VerifyDiagnostics( - // (10,19): error CS9504: '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 CS9504: '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) - ); + var comp = CreateCompilation(source, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "[C1.operator][C2.operator]").VerifyDiagnostics(); } [Fact] - public void CompoundAssignment_00990_Implement_RegularVsOperatorMismatch() + public void CompoundAssignment_01040_Consumption_Overriding() { 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 abstract class C1 +{ + public abstract void operator +=(int x); } -public class C3 : C2, I1 +public class C2 : C1 { - public virtual void op_AdditionAssignment(int x) + public override void operator +=(int x) { - System.Console.Write(""[C3.op_Increment]""); + System.Console.Write(""[C2.operator]""); } } @@ -13837,345 +14890,738 @@ public class Program { static void Main() { - I1 x = new C3(); + C1 x = new C2(); x += 1; } } "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); - comp.VerifyDiagnostics( - // (15,23): error CS9504: '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) - ); + CompileAndVerify(comp, expectedOutput: "[C2.operator]").VerifyDiagnostics(); } [Fact] - public void CompoundAssignment_01000_Implement_RegularVsOperatorMismatch() + public void CompoundAssignment_01050_Shadow_RegularVsOperatorMismatch() { var source = @" -public interface I1 +public class C1 { - public void op_AdditionAssignment(int x); + public void operator +=(int x){} } -public class C2 : I1 +public class C2 : C1 { - void I1.op_AdditionAssignment(int x) - { - System.Console.Write(""[C2.op_Increment]""); - } + public void op_AdditionAssignment(int x){} } +"; -public class C3 : C2, I1 + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void CompoundAssignment_01060_Shadow_RegularVsOperatorMismatch() + { + var source = @" +public class C2 { - public virtual void operator +=(int x) - { - System.Console.Write(""[C3.operator]""); - } + public void op_AdditionAssignment(int x){} } -public class Program +public class C1 : C2 { - static void Main() - { - I1 x = new C3(); - x.op_AdditionAssignment(1); - } + public void operator +=(int x){} } "; - var comp = CreateCompilation(source, options: TestOptions.DebugExe); - comp.VerifyDiagnostics( - // (15,23): error CS9504: '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) - ); + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); } - [Fact] - public void CompoundAssignment_01010_Implement_RegularVsOperatorMismatch() + private static string ToCRefOp(string op) { - /* - public interface I1 - { - public void operator+=(int x); - } + return op.Replace("&", "&").Replace("<", "<"); + } - 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 + [Theory] + [CombinatorialData] + public void CompoundAssignment_01070_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + { + var source = @" +/// +/// See . +/// +class C1 { - .method public hidebysig newslot abstract virtual specialname - instance void op_AdditionAssignment (int32 x) cil managed - { - } + public void operator " + op + @"(int x) {} + public void operator " + op + @"(long x) {} } -.class public auto ansi beforefieldinit C1 - extends [mscorlib]System.Object - implements I1 +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, 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 . + 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 . + 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 = @" +/// +/// See . +/// +class C1 { - .method public hidebysig newslot virtual - instance void op_AdditionAssignment (int32 x) cil managed - { - .maxstack 8 + public void operator " + op + @"(int x) {} +} - IL_0000: ldc.i4.1 - IL_0001: call void [mscorlib]System.Console::Write(int32) - IL_0006: ret - } +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); - .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 - } + 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 = @" +/// +/// See . +/// +class C1 +{ + public void operator " + op + @"(int x) {} + public void operator " + op + @"(long x) {} } + +/// +/// See . +/// +class C2 +{} "; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); - var source1 = -@" -public class C2 : C1, I1 + 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 . + 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 . + 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 = @" +/// +/// See . +/// +class C1 { + public void operator " + op + @"(long x) {} + public void operator " + op + @"(int x) {} } + +/// +/// See . +/// +class C2 +{} "; - var compilation1 = CreateCompilationWithIL(source1, ilSource); + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); - compilation1.VerifyDiagnostics( - // (2,23): error CS9504: '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) - ); + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) + { + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation); + AssertEx.Equal("C1.operator " + op + @"(int)", actualSymbol.ToDisplayString()); + count++; + } - var source2 = -@" -class Program + Assert.Equal(2, count); + } + + [Theory] + [CombinatorialData] + public void CompoundAssignment_01130_CRef([CombinatorialValues("+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + { + var source = @" +/// +/// See . +/// +class C1 +{ + public void operator " + op + @"(int x) {} + public void operator " + op + @"(long x) {} +} + +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, 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 . + 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 . + 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 = @" +/// +/// See . +/// +class C1 +{ + public void operator " + op + @"(int x) {} + public void operator " + op + @"(long x) {} +} + +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, 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 . + 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 . + 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 = @" +/// +/// See . +/// +class C1 +{ + public static void " + CompoundAssignmentOperatorName(op, isChecked: false) + @"(int x, long y) {} +} + +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, 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 = @" +/// +/// See . +/// +class C1 +{ + public void operator " + op + @"(int x) {} +} + +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, 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 = @" +/// +/// See . +/// +class C1 { - static void Main() - { - var c1 = new C1(); - c1.op_AdditionAssignment(1); - I1 x = c1; - x += 1; - } + 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 +/// +/// See . +/// +class C2 +{} "; - var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe); + var compilation = CreateCompilation(source, 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 . + 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 . + Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + ToCRefOp(op) + @"()").WithArguments("operator checked " + ToCRefOp(op) + @"()").WithLocation(12, 20) + }; - CompileAndVerify(compilation2, expectedOutput: "11", verify: Verification.Skipped).VerifyDiagnostics(); + compilation.VerifyDiagnostics(expected); - var i1M1 = compilation1.GetTypeByMetadataName("I1").GetMembers().Single(); - var c1 = compilation1.GetTypeByMetadataName("C1"); + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) + { + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation, expected[count]); + Assert.Null(actualSymbol); + count++; + } - AssertEx.Equal("C1.op_AdditionAssignment(int)", c1.FindImplementationForInterfaceMember(i1M1).ToDisplayString()); + Assert.Equal(2, count); } - [Fact] - public void CompoundAssignment_01020_Implement_RegularVsOperatorMismatch() + [Theory] + [CombinatorialData] + public void CompoundAssignment_01180_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { - /* - 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 + var source = @" +/// +/// See . +/// +class C1 { - .method public hidebysig newslot abstract virtual - instance void op_AdditionAssignment (int32 x) cil managed - { - } + public void operator " + op + @"(int x) {} + public void operator checked " + op + @"(int x) {} } -.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 +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); - IL_0000: ldc.i4.1 - IL_0001: call void [mscorlib]System.Console::Write(int32) - IL_0006: ret - } + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) + { + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation); + AssertEx.Equal("C1.operator checked " + op + @"(int)", actualSymbol.ToDisplayString()); + count++; + } - .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 - } -} -"; + Assert.Equal(2, count); + } - var source1 = -@" -public class C2 : C1, I1 + [Theory] + [CombinatorialData] + public void CompoundAssignment_01181_CRef_Checked([CombinatorialValues("%=", "&=", "|=", "^=", "<<=", ">>=", ">>>=")] string op) + { + var source = @" +/// +/// See . +/// +class C1 { + public void operator " + op + @"(int x) {} } + +#line 11 +/// +/// See . +/// +class C2 +{} "; - var compilation1 = CreateCompilationWithIL(source1, ilSource); + var compilation = CreateCompilation(source, 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 . + 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 . + Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + ToCRefOp(op)).WithArguments("operator checked " + ToCRefOp(op)).WithLocation(12, 20) + }; - compilation1.VerifyDiagnostics( - // (2,23): error CS9504: '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) - ); + compilation.VerifyDiagnostics(expected); - var source2 = -@" -class Program + 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 = @" +/// +/// See . +/// +class C1 { - static void Main() - { - var c1 = new C1(); - c1 += 1; - I1 x = c1; - x.op_AdditionAssignment(1); - } + 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 +/// +/// See . +/// +class C2 +{} "; - var compilation2 = CreateCompilationWithIL(source2, ilSource, options: TestOptions.DebugExe); + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); - CompileAndVerify(compilation2, expectedOutput: "11", verify: Verification.Skipped).VerifyDiagnostics(); + 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 . + 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 . + 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) + }; - var i1M1 = compilation1.GetTypeByMetadataName("I1").GetMembers().Single(); - var c1 = compilation1.GetTypeByMetadataName("C1"); + compilation.VerifyDiagnostics(expected); - AssertEx.Equal("C1.operator +=(int)", c1.FindImplementationForInterfaceMember(i1M1).ToDisplayString()); + 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); } - [Fact] - public void CompoundAssignment_01030_Consumption_Implementation() + [Theory] + [CombinatorialData] + public void CompoundAssignment_01220_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -public interface I1 +/// +/// See . +/// +class C1 { - public void operator +=(int x); + 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) {} } -public class C1 : I1 -{ - public void operator +=(int x) - { - System.Console.Write(""[C1.operator]""); - } -} +/// +/// See . +/// +class C2 +{} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); -public class C2 : I1 -{ - void I1.operator +=(int x) - { - System.Console.Write(""[C2.operator]""); - } -} + int count = 0; + foreach (var crefSyntax in GetCrefSyntaxes(compilation)) + { + var actualSymbol = GetReferencedSymbol(crefSyntax, compilation); + AssertEx.Equal("C1.operator checked " + op + @"(int)", actualSymbol.ToDisplayString()); + count++; + } -public class Program + Assert.Equal(2, count); + } + + [Theory] + [CombinatorialData] + public void CompoundAssignment_01230_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) + { + var source = @" +/// +/// See . +/// +class C1 { - static void Main() - { - I1 x = new C1(); - x += 1; - x = new C2(); - x += 1; - } + 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 +/// +/// See . +/// +class C2 +{} "; + var compilation = CreateCompilation(source, 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 . + 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 . + Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + ToCRefOp(op) + @"(string)").WithArguments("operator checked " + ToCRefOp(op) + @"(string)").WithLocation(12, 20) + }; - var comp = CreateCompilation(source, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "[C1.operator][C2.operator]").VerifyDiagnostics(); + 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); } - [Fact] - public void CompoundAssignment_01040_Consumption_Overriding() + [Theory] + [CombinatorialData] + public void CompoundAssignment_01231_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -public abstract class C1 -{ - public abstract void operator +=(int x); -} - -public class C2 : C1 +/// +/// See . +/// +class C1 { - public override void operator +=(int x) - { - System.Console.Write(""[C2.operator]""); - } + 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) {} } -public class Program -{ - static void Main() - { - C1 x = new C2(); - x += 1; - } -} +#line 11 +/// +/// See . +/// +class C2 +{} "; + var compilation = CreateCompilation(source, 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 . + 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 . + Diagnostic(ErrorCode.WRN_BadXMLRef, "C1.operator checked " + ToCRefOp(op) + @"(int, string)").WithArguments("operator checked " + ToCRefOp(op) + @"(int, string)").WithLocation(12, 20) + }; - var comp = CreateCompilation(source, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "[C2.operator]").VerifyDiagnostics(); + 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); } - [Fact] - public void CompoundAssignment_01050_Shadow_RegularVsOperatorMismatch() + [Theory] + [CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/78103")] + public void CompoundAssignment_01240_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -public class C1 +/// +/// See . +/// +class C1 { - public void operator +=(int x){} + public static void " + CompoundAssignmentOperatorName(op, isChecked: true) + @"(int x, long y) {} } -public class C2 : C1 -{ - public void op_AdditionAssignment(int x){} -} +/// +/// See . +/// +class C2 +{} "; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics(); + 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); } - [Fact] - public void CompoundAssignment_01060_Shadow_RegularVsOperatorMismatch() + [Theory] + [CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/78103")] + public void CompoundAssignment_01260_CRef_Checked([CombinatorialValues("+=", "-=", "*=", "/=")] string op) { var source = @" -public class C2 +/// +/// See . +/// +class C1 { - public void op_AdditionAssignment(int x){} + public void operator " + op + @"(int x) {} + public void operator checked " + op + @"(int x) {} } -public class C1 : C2 -{ - public void operator +=(int x){} -} +/// +/// See . +/// +class C2 +{} "; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); + compilation.VerifyDiagnostics(); - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics(); + 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); } // PROTOTYPE: Disable ORPA during overload resolution? diff --git a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs index f41a0edd45f3a..528ab45849cb5 100644 --- a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs @@ -6606,39 +6606,6 @@ class Cat { } #endregion Dev10 bugs from KevinH - private static IEnumerable GetCrefSyntaxes(Compilation compilation) => GetCrefSyntaxes((CSharpCompilation)compilation); - - internal static IEnumerable GetCrefSyntaxes(CSharpCompilation compilation) - { - return compilation.SyntaxTrees.SelectMany(tree => - { - var docComments = tree.GetCompilationUnitRoot().DescendantTrivia().Select(trivia => trivia.GetStructure()).OfType(); - return docComments.SelectMany(docComment => docComment.DescendantNodes().OfType().Select(attr => attr.Cref)); - }); - } - - internal static Symbol GetReferencedSymbol(CrefSyntax crefSyntax, CSharpCompilation compilation, params DiagnosticDescription[] expectedDiagnostics) - { - Symbol ambiguityWinner; - var references = GetReferencedSymbols(crefSyntax, compilation, out ambiguityWinner, expectedDiagnostics); - Assert.Null(ambiguityWinner); - Assert.InRange(references.Length, 0, 1); //Otherwise, call GetReferencedSymbols - - return references.FirstOrDefault(); - } - - private static ImmutableArray GetReferencedSymbols(CrefSyntax crefSyntax, CSharpCompilation compilation, out Symbol ambiguityWinner, params DiagnosticDescription[] expectedDiagnostics) - { - var binderFactory = compilation.GetBinderFactory(crefSyntax.SyntaxTree); - var binder = binderFactory.GetBinder(crefSyntax); - - DiagnosticBag diagnostics = DiagnosticBag.GetInstance(); - var references = binder.BindCref(crefSyntax, out ambiguityWinner, diagnostics); - diagnostics.Verify(expectedDiagnostics); - diagnostics.Free(); - return references; - } - private static ISymbol[] GetCrefOriginalDefinitions(SemanticModel model, IEnumerable crefs) { return crefs.Select(syntax => model.GetSymbolInfo(syntax).Symbol).Select(symbol => (object)symbol == null ? null : symbol.OriginalDefinition).ToArray(); diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 2fd9e0832aaef..c026a29e28326 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -1903,6 +1903,39 @@ internal static string GetDocumentationCommentText(CSharpCompilation compilation } } + internal static IEnumerable GetCrefSyntaxes(Compilation compilation) => GetCrefSyntaxes((CSharpCompilation)compilation); + + internal static IEnumerable GetCrefSyntaxes(CSharpCompilation compilation) + { + return compilation.SyntaxTrees.SelectMany(tree => + { + var docComments = tree.GetCompilationUnitRoot().DescendantTrivia().Select(trivia => trivia.GetStructure()).OfType(); + return docComments.SelectMany(docComment => docComment.DescendantNodes().OfType().Select(attr => attr.Cref)); + }); + } + + internal static Symbol GetReferencedSymbol(CrefSyntax crefSyntax, CSharpCompilation compilation, params DiagnosticDescription[] expectedDiagnostics) + { + Symbol ambiguityWinner; + var references = GetReferencedSymbols(crefSyntax, compilation, out ambiguityWinner, expectedDiagnostics); + Assert.Null(ambiguityWinner); + Assert.InRange(references.Length, 0, 1); //Otherwise, call GetReferencedSymbols + + return references.FirstOrDefault(); + } + + internal static ImmutableArray GetReferencedSymbols(CrefSyntax crefSyntax, CSharpCompilation compilation, out Symbol ambiguityWinner, params DiagnosticDescription[] expectedDiagnostics) + { + var binderFactory = compilation.GetBinderFactory(crefSyntax.SyntaxTree); + var binder = binderFactory.GetBinder(crefSyntax); + + DiagnosticBag diagnostics = DiagnosticBag.GetInstance(); + var references = binder.BindCref(crefSyntax, out ambiguityWinner, diagnostics); + diagnostics.Verify(expectedDiagnostics); + diagnostics.Free(); + return references; + } + #endregion #region IL Validation