From 7f4d08402ec16f3887ba3e0b8de151bc863c5b92 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 21 Oct 2025 11:58:24 -0700 Subject: [PATCH 1/6] Extensions: improve diagnostics for operators --- .../Binder.OperatorResolutionForReporting.cs | 340 +++++ .../Portable/Binder/Binder_Operators.cs | 1201 +++++++++-------- .../Portable/Binder/Binder_Statements.cs | 5 +- .../Portable/Binder/Binder_TupleOperators.cs | 10 +- .../Operators/BinaryOperatorAnalysisResult.cs | 8 +- .../Operators/OperatorAnalysisResultKind.cs | 4 - .../Operators/UnaryOperatorAnalysisResult.cs | 7 + .../CSharp/Portable/CSharpResources.resx | 9 + .../CSharp/Portable/Errors/ErrorCode.cs | 3 + .../CSharp/Portable/Errors/ErrorFacts.cs | 3 + .../Portable/xlf/CSharpResources.cs.xlf | 15 + .../Portable/xlf/CSharpResources.de.xlf | 15 + .../Portable/xlf/CSharpResources.es.xlf | 15 + .../Portable/xlf/CSharpResources.fr.xlf | 15 + .../Portable/xlf/CSharpResources.it.xlf | 15 + .../Portable/xlf/CSharpResources.ja.xlf | 15 + .../Portable/xlf/CSharpResources.ko.xlf | 15 + .../Portable/xlf/CSharpResources.pl.xlf | 15 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 15 + .../Portable/xlf/CSharpResources.ru.xlf | 15 + .../Portable/xlf/CSharpResources.tr.xlf | 15 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 15 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 15 + .../Emit3/OverloadResolutionPriorityTests.cs | 4 +- .../Semantics/ExtensionOperatorsTests.cs | 1037 ++++++++++++-- .../Test/Emit3/Semantics/ExtensionTests2.cs | 8 +- ...DefinedCompoundAssignmentOperatorsTests.cs | 4 +- ...perationTests_IBinaryOperatorExpression.cs | 21 +- .../Semantic/Semantics/DelegateTypeTests.cs | 8 +- .../Semantics/NullableReferenceTypesTests.cs | 8 +- .../Test/Semantic/Semantics/OperatorTests.cs | 34 +- .../Semantics/OverloadResolutionPerfTests.cs | 4 +- .../Semantics/OverloadResolutionTests.cs | 10 +- .../DefaultInterfaceImplementationTests.cs | 32 +- 34 files changed, 2223 insertions(+), 732 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs b/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs new file mode 100644 index 0000000000000..418c4cf3c3217 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs @@ -0,0 +1,340 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.CSharp; + +internal partial class Binder +{ + /// + /// This type collects different kinds of results from operator scenarios and provides a unified way to report diagnostics. + /// It collects the first non-empty result for extensions and non-extensions separately. + /// This follows a similar logic to ResolveMethodGroupInternal and OverloadResolutionResult.ReportDiagnostics + /// + private struct OperatorResolutionForReporting + { + private object? _nonExtensionResult; + private object? _extensionResult; + + /// Returns true if the result was set and took ownership of the result. + private bool SaveResult(object result, ref object? savedResult) + { + if (savedResult is null) + { + savedResult = result; + AssertInvariant(); + return true; + } + + return false; + } + + /// Returns true if the result was set and took ownership of the result. + public bool SaveResult(OverloadResolutionResult result, bool isExtension) + { + if (result.ResultsBuilder.IsEmpty) + { + return false; + } + + return SaveResult(result, ref isExtension ? ref _extensionResult : ref _nonExtensionResult); + } + + /// Returns true if the result was set and took ownership of the result. + public bool SaveResult(BinaryOperatorOverloadResolutionResult result, bool isExtension) + { + if (result.Results.IsEmpty) + { + return false; + } + + return SaveResult(result, ref isExtension ? ref _extensionResult : ref _nonExtensionResult); + } + + /// Returns true if the result was set and took ownership of the result. + public bool SaveResult(UnaryOperatorOverloadResolutionResult result, bool isExtension) + { + if (result.Results.IsEmpty) + { + return false; + } + + return SaveResult(result, ref isExtension ? ref _extensionResult : ref _nonExtensionResult); + } + + /// + /// Follows a very simplified version of OverloadResolutionResult.ReportDiagnostics which can be expanded in the future if needed. + /// + internal bool TryReportDiagnostics(SyntaxNode node, BindingDiagnosticBag diagnostics, Binder binder, object leftDisplay, object? rightDisplay) + { + object? resultToUse = pickResultToUse(_nonExtensionResult, _extensionResult); + if (resultToUse is null) + { + return false; + } + + var results = ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKind)>.GetInstance(); + populateResults(results, resultToUse); + + bool reported = tryReportDiagnostics(node, diagnostics, binder, results, leftDisplay, rightDisplay); + results.Free(); + + return reported; + + static bool tryReportDiagnostics(SyntaxNode node, BindingDiagnosticBag diagnostics, Binder binder, ArrayBuilder<(MethodSymbol? member, OperatorAnalysisResultKind resultKind)> results, object leftDisplay, object? rightDisplay) + { + assertNone(results, OperatorAnalysisResultKind.Undefined); + + if (hadAmbiguousBestMethods(results, node, diagnostics, binder)) + { + return true; + } + + if (results.Any(m => m.resultKind == OperatorAnalysisResultKind.Applicable)) + { + return false; + } + + assertNone(results, OperatorAnalysisResultKind.Applicable); + assertNone(results, OperatorAnalysisResultKind.Worse); + + Debug.Assert(results.All(r => r.resultKind == OperatorAnalysisResultKind.Inapplicable)); + + // There is much room to improve diagnostics on inapplicable candidates, but for now we just report the candidate if there is a single one. + if (results.Count == 1 && results[0].member is { } inapplicableMember) + { + var toReport = nodeToReport(node); + if (rightDisplay is null) + { + // error: Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Error(diagnostics, ErrorCode.ERR_SingleInapplicableUnaryOperator, toReport, leftDisplay, inapplicableMember); + } + else + { + // error: Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Error(diagnostics, ErrorCode.ERR_SingleInapplicableBinaryOperator, toReport, leftDisplay, rightDisplay, inapplicableMember); + } + + return true; + } + + return false; + } + + static object? pickResultToUse(object? nonExtensionResult, object? extensionResult) + { + if (nonExtensionResult is null) + { + return extensionResult; + } + + if (extensionResult is null) + { + return nonExtensionResult; + } + + bool useNonExtension = getBestKind(nonExtensionResult) >= getBestKind(extensionResult); + return useNonExtension ? nonExtensionResult : extensionResult; + } + + static OperatorAnalysisResultKind getBestKind(object result) + { + OperatorAnalysisResultKind bestKind = OperatorAnalysisResultKind.Undefined; + + switch (result) + { + case OverloadResolutionResult r1: + foreach (var res in r1.ResultsBuilder) + { + var kind = mapKind(res.Result.Kind); + if (kind > bestKind) + { + bestKind = kind; + } + } + break; + + case BinaryOperatorOverloadResolutionResult r2: + foreach (var res in r2.Results) + { + if (res.Signature.Method is null) + { + // Skip build-in operators + continue; + } + + if (res.Kind > bestKind) + { + bestKind = res.Kind; + } + } + break; + + case UnaryOperatorOverloadResolutionResult r3: + foreach (var res in r3.Results) + { + if (res.Signature.Method is null) + { + // Skip build-in operators + continue; + } + + if (res.Kind > bestKind) + { + bestKind = res.Kind; + } + } + break; + + default: + throw ExceptionUtilities.UnexpectedValue(result); + } + + return bestKind; + } + + static bool hadAmbiguousBestMethods(ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKind)> results, SyntaxNode node, BindingDiagnosticBag diagnostics, Binder binder) + { + if (!tryGetTwoBest(results, out var first, out var second)) + { + return false; + } + + Error(diagnostics, ErrorCode.ERR_AmbigOperator, nodeToReport(node), first, second); + return true; + } + + static SyntaxNodeOrToken nodeToReport(SyntaxNode node) + { + return node switch + { + AssignmentExpressionSyntax assignment => assignment.OperatorToken, + BinaryExpressionSyntax binary => binary.OperatorToken, + PrefixUnaryExpressionSyntax prefix => prefix.OperatorToken, + PostfixUnaryExpressionSyntax postfix => postfix.OperatorToken, + _ => node + }; + } + + static void assertNone(ArrayBuilder<(MethodSymbol? member, OperatorAnalysisResultKind resultKind)> results, OperatorAnalysisResultKind kind) + { + Debug.Assert(results.All(r => r.resultKind != kind)); + } + + static bool tryGetTwoBest(ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKind)> results, [NotNullWhen(true)] out MethodSymbol? first, [NotNullWhen(true)] out MethodSymbol? second) + { + first = null; + second = null; + bool foundFirst = false; + + foreach (var (member, resultKind) in results) + { + if (member is null) + { + continue; + } + + if (resultKind == OperatorAnalysisResultKind.Applicable) + { + if (!foundFirst) + { + first = member; + foundFirst = true; + } + else + { + Debug.Assert(first is not null); + second = member; + return true; + } + } + } + + return false; + } + + static void populateResults(ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKind)> results, object? extensionResult) + { + switch (extensionResult) + { + case OverloadResolutionResult result1: + foreach (var res in result1.ResultsBuilder) + { + OperatorAnalysisResultKind kind = mapKind(res.Result.Kind); + + results.Add((res.Member, kind)); + } + break; + + case BinaryOperatorOverloadResolutionResult result2: + foreach (var res in result2.Results) + { + results.Add((res.Signature.Method, res.Kind)); + } + break; + + case UnaryOperatorOverloadResolutionResult result3: + foreach (var res in result3.Results) + { + results.Add((res.Signature.Method, res.Kind)); + } + break; + + default: + throw ExceptionUtilities.UnexpectedValue(extensionResult); + } + } + + static OperatorAnalysisResultKind mapKind(MemberResolutionKind kind) + { + return kind switch + { + MemberResolutionKind.ApplicableInExpandedForm => OperatorAnalysisResultKind.Applicable, + MemberResolutionKind.ApplicableInNormalForm => OperatorAnalysisResultKind.Applicable, + MemberResolutionKind.Worse => OperatorAnalysisResultKind.Worse, + MemberResolutionKind.Worst => OperatorAnalysisResultKind.Worse, + _ => OperatorAnalysisResultKind.Inapplicable, + }; + } + } + + internal void Free() + { + free(ref _nonExtensionResult); + free(ref _extensionResult); + + static void free(ref object? result) + { + switch (result) + { + case null: + return; + case OverloadResolutionResult result1: + result1.Free(); + break; + case BinaryOperatorOverloadResolutionResult result2: + result2.Free(); + break; + case UnaryOperatorOverloadResolutionResult result3: + result3.Free(); + break; + } + + result = null; + } + } + + [Conditional("DEBUG")] + private void AssertInvariant() + { + Debug.Assert(_nonExtensionResult is null or OverloadResolutionResult or BinaryOperatorOverloadResolutionResult or UnaryOperatorOverloadResolutionResult); + Debug.Assert(_extensionResult is null or OverloadResolutionResult or BinaryOperatorOverloadResolutionResult or UnaryOperatorOverloadResolutionResult); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 36d9285129279..ef60e0faf67a5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -23,294 +23,304 @@ internal partial class Binder private BoundExpression BindCompoundAssignment(AssignmentExpressionSyntax node, BindingDiagnosticBag diagnostics) { - node.Left.CheckDeconstructionCompatibleArgument(diagnostics); + OperatorResolutionForReporting operatorResolutionForReporting = default; + BoundExpression result = bindCompoundAssignment(node, diagnostics, ref operatorResolutionForReporting); + operatorResolutionForReporting.Free(); + return result; - BoundExpression left = BindValue(node.Left, diagnostics, GetBinaryAssignmentKind(node.Kind())); - ReportSuppressionIfNeeded(left, diagnostics); - BoundExpression right = BindValue(node.Right, diagnostics, BindValueKind.RValue); - BinaryOperatorKind kind = SyntaxKindToBinaryOperatorKind(node.Kind()); + BoundExpression bindCompoundAssignment(AssignmentExpressionSyntax node, BindingDiagnosticBag diagnostics, ref OperatorResolutionForReporting operatorResolutionForReporting) + { + node.Left.CheckDeconstructionCompatibleArgument(diagnostics); - // If either operand is bad, don't try to do binary operator overload resolution; that will just - // make cascading errors. + BoundExpression left = BindValue(node.Left, diagnostics, GetBinaryAssignmentKind(node.Kind())); + ReportSuppressionIfNeeded(left, diagnostics); + BoundExpression right = BindValue(node.Right, diagnostics, BindValueKind.RValue); + BinaryOperatorKind kind = SyntaxKindToBinaryOperatorKind(node.Kind()); - if (left.Kind == BoundKind.EventAccess) - { - BinaryOperatorKind kindOperator = kind.Operator(); - switch (kindOperator) + // If either operand is bad, don't try to do binary operator overload resolution; that will just + // make cascading errors. + + if (left.Kind == BoundKind.EventAccess) { - case BinaryOperatorKind.Addition: - case BinaryOperatorKind.Subtraction: - return BindEventAssignment(node, (BoundEventAccess)left, right, kindOperator, diagnostics); + BinaryOperatorKind kindOperator = kind.Operator(); + switch (kindOperator) + { + case BinaryOperatorKind.Addition: + case BinaryOperatorKind.Subtraction: + return BindEventAssignment(node, (BoundEventAccess)left, right, kindOperator, diagnostics); - // fall-through for other operators, if RHS is dynamic we produce dynamic operation, otherwise we'll report an error ... + // fall-through for other operators, if RHS is dynamic we produce dynamic operation, otherwise we'll report an error ... + } } - } - if (left.HasAnyErrors || right.HasAnyErrors) - { - // NOTE: no overload resolution candidates. - left = BindToTypeForErrorRecovery(left); - right = BindToTypeForErrorRecovery(right); - return new BoundCompoundAssignmentOperator(node, BinaryOperatorSignature.Error, left, right, - leftPlaceholder: null, leftConversion: null, finalPlaceholder: null, finalConversion: null, LookupResultKind.Empty, CreateErrorType(), hasErrors: true); - } + if (left.HasAnyErrors || right.HasAnyErrors) + { + // NOTE: no overload resolution candidates. + left = BindToTypeForErrorRecovery(left); + right = BindToTypeForErrorRecovery(right); + return new BoundCompoundAssignmentOperator(node, BinaryOperatorSignature.Error, left, right, + leftPlaceholder: null, leftConversion: null, finalPlaceholder: null, finalConversion: null, LookupResultKind.Empty, CreateErrorType(), hasErrors: true); + } - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - if (left.HasDynamicType() || right.HasDynamicType()) - { - if (IsLegalDynamicOperand(right) && IsLegalDynamicOperand(left) && kind != BinaryOperatorKind.UnsignedRightShift) + if (left.HasDynamicType() || right.HasDynamicType()) { - left = BindToNaturalType(left, diagnostics); - Debug.Assert(left.Type is { }); + if (IsLegalDynamicOperand(right) && IsLegalDynamicOperand(left) && kind != BinaryOperatorKind.UnsignedRightShift) + { + left = BindToNaturalType(left, diagnostics); + Debug.Assert(left.Type is { }); - right = BindToNaturalType(right, diagnostics); - var placeholder = new BoundValuePlaceholder(right.Syntax, left.HasDynamicType() ? left.Type : right.Type).MakeCompilerGenerated(); - var finalDynamicConversion = this.Compilation.Conversions.ClassifyConversionFromExpression(placeholder, left.Type, isChecked: CheckOverflowAtRuntime, ref useSiteInfo); - diagnostics.Add(node, useSiteInfo); - var conversion = (BoundConversion)CreateConversion(node, placeholder, finalDynamicConversion, isCast: true, conversionGroupOpt: null, left.Type, diagnostics); + right = BindToNaturalType(right, diagnostics); + var placeholder = new BoundValuePlaceholder(right.Syntax, left.HasDynamicType() ? left.Type : right.Type).MakeCompilerGenerated(); + var finalDynamicConversion = this.Compilation.Conversions.ClassifyConversionFromExpression(placeholder, left.Type, isChecked: CheckOverflowAtRuntime, ref useSiteInfo); + diagnostics.Add(node, useSiteInfo); + var conversion = (BoundConversion)CreateConversion(node, placeholder, finalDynamicConversion, isCast: true, conversionGroupOpt: null, left.Type, diagnostics); - conversion = conversion.Update(conversion.Operand, conversion.Conversion, conversion.IsBaseConversion, conversion.Checked, - explicitCastInCode: true, conversion.ConstantValueOpt, conversion.ConversionGroupOpt, conversion.Type); + conversion = conversion.Update(conversion.Operand, conversion.Conversion, conversion.IsBaseConversion, conversion.Checked, + explicitCastInCode: true, conversion.ConstantValueOpt, conversion.ConversionGroupOpt, conversion.Type); - return new BoundCompoundAssignmentOperator( - node, - new BinaryOperatorSignature( - kind.WithType(BinaryOperatorKind.Dynamic).WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), + return new BoundCompoundAssignmentOperator( + node, + new BinaryOperatorSignature( + kind.WithType(BinaryOperatorKind.Dynamic).WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), + left.Type, + right.Type, + Compilation.DynamicType), + left, + right, + leftPlaceholder: null, leftConversion: null, + finalPlaceholder: placeholder, + finalConversion: conversion, + LookupResultKind.Viable, left.Type, - right.Type, - Compilation.DynamicType), - left, - right, - leftPlaceholder: null, leftConversion: null, - finalPlaceholder: placeholder, - finalConversion: conversion, - LookupResultKind.Viable, - left.Type, - hasErrors: false); + hasErrors: false); + } + else + { + Error(diagnostics, ErrorCode.ERR_BadBinaryOps, node, node.OperatorToken.Text, left.Display, right.Display); + + // error: operator can't be applied on dynamic and a type that is not convertible to dynamic: + left = BindToTypeForErrorRecovery(left); + right = BindToTypeForErrorRecovery(right); + return new BoundCompoundAssignmentOperator(node, BinaryOperatorSignature.Error, left, right, + leftPlaceholder: null, leftConversion: null, finalPlaceholder: null, finalConversion: null, LookupResultKind.Empty, CreateErrorType(), hasErrors: true); + } } - else + + if (left.Kind == BoundKind.EventAccess && !CheckEventValueKind((BoundEventAccess)left, BindValueKind.Assignable, diagnostics)) { - Error(diagnostics, ErrorCode.ERR_BadBinaryOps, node, node.OperatorToken.Text, left.Display, right.Display); + // If we're in a place where the event can be assigned, then continue so that we give errors + // about the types and operator not lining up. Otherwise, just report that the event can't + // be used here. - // error: operator can't be applied on dynamic and a type that is not convertible to dynamic: + // NOTE: no overload resolution candidates. left = BindToTypeForErrorRecovery(left); right = BindToTypeForErrorRecovery(right); return new BoundCompoundAssignmentOperator(node, BinaryOperatorSignature.Error, left, right, - leftPlaceholder: null, leftConversion: null, finalPlaceholder: null, finalConversion: null, LookupResultKind.Empty, CreateErrorType(), hasErrors: true); + leftPlaceholder: null, leftConversion: null, finalPlaceholder: null, finalConversion: null, LookupResultKind.NotAVariable, CreateErrorType(), hasErrors: true); } - } - if (left.Kind == BoundKind.EventAccess && !CheckEventValueKind((BoundEventAccess)left, BindValueKind.Assignable, diagnostics)) - { - // If we're in a place where the event can be assigned, then continue so that we give errors - // about the types and operator not lining up. Otherwise, just report that the event can't - // be used here. + if (!IsTypelessExpressionAllowedInBinaryOperator(kind, left, right)) + { + return createBadCompoundAssignmentOperator(node, kind, left, right, LookupResultKind.OverloadResolutionFailure, originalUserDefinedOperators: default, ref operatorResolutionForReporting, diagnostics); + } - // NOTE: no overload resolution candidates. - left = BindToTypeForErrorRecovery(left); - right = BindToTypeForErrorRecovery(right); - return new BoundCompoundAssignmentOperator(node, BinaryOperatorSignature.Error, left, right, - leftPlaceholder: null, leftConversion: null, finalPlaceholder: null, finalConversion: null, LookupResultKind.NotAVariable, CreateErrorType(), hasErrors: true); - } + bool checkOverflowAtRuntime = CheckOverflowAtRuntime; - if (!IsTypelessExpressionAllowedInBinaryOperator(kind, left, right)) - { - return createBadCompoundAssignmentOperator(node, kind, left, right, LookupResultKind.OverloadResolutionFailure, default(ImmutableArray), diagnostics); - } + // Try an in-place user-defined operator + bool tryInstance = shouldTryUserDefinedInstanceOperator(node, checkOverflowAtRuntime, left, out string? checkedInstanceOperatorName, out string? ordinaryInstanceOperatorName); - bool checkOverflowAtRuntime = CheckOverflowAtRuntime; + if (tryInstance) + { + Debug.Assert(ordinaryInstanceOperatorName is not null); - // Try an in-place user-defined operator - bool tryInstance = shouldTryUserDefinedInstanceOperator(node, checkOverflowAtRuntime, left, out string? checkedInstanceOperatorName, out string? ordinaryInstanceOperatorName); + BoundCompoundAssignmentOperator? inPlaceResult = tryApplyUserDefinedInstanceOperator(node, kind, checkOverflowAtRuntime, checkedInstanceOperatorName, ordinaryInstanceOperatorName, + left, right, ref operatorResolutionForReporting, diagnostics); + if (inPlaceResult is not null) + { + return inPlaceResult; + } + } - if (tryInstance) - { - Debug.Assert(ordinaryInstanceOperatorName is not null); + // A compound operator, say, x |= y, is bound as x = (X)( ((T)x) | ((T)y) ). We must determine + // the binary operator kind, the type conversions from each side to the types expected by + // the operator, and the type conversion from the return type of the operand to the left hand side. + // + // We can get away with binding the right-hand-side of the operand into its converted form early. + // This is convenient because first, it is never rewritten into an access to a temporary before + // the conversion, and second, because that is more convenient for the "d += lambda" case. + // We want to have the converted (bound) lambda in the bound tree, not the unconverted unbound lambda. - BoundCompoundAssignmentOperator? inPlaceResult = tryApplyUserDefinedInstanceOperator(node, kind, checkOverflowAtRuntime, checkedInstanceOperatorName, ordinaryInstanceOperatorName, - left, right, diagnostics); - if (inPlaceResult is not null) - { - return inPlaceResult; - } - } + LookupResultKind resultKind; + ImmutableArray originalUserDefinedOperators; - // A compound operator, say, x |= y, is bound as x = (X)( ((T)x) | ((T)y) ). We must determine - // the binary operator kind, the type conversions from each side to the types expected by - // the operator, and the type conversion from the return type of the operand to the left hand side. - // - // We can get away with binding the right-hand-side of the operand into its converted form early. - // This is convenient because first, it is never rewritten into an access to a temporary before - // the conversion, and second, because that is more convenient for the "d += lambda" case. - // We want to have the converted (bound) lambda in the bound tree, not the unconverted unbound lambda. + OverloadResolution.GetStaticUserDefinedBinaryOperatorMethodNames(kind, checkOverflowAtRuntime, out string staticOperatorName1, out string? staticOperatorName2Opt); + BinaryOperatorAnalysisResult best = BinaryOperatorNonExtensionOverloadResolution(kind, isChecked: checkOverflowAtRuntime, staticOperatorName1, staticOperatorName2Opt, left, right, + node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); - LookupResultKind resultKind; - ImmutableArray originalUserDefinedOperators; + Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); + Debug.Assert(best.HasValue == (resultKind is LookupResultKind.Viable)); + Debug.Assert(resultKind is not (LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty) || originalUserDefinedOperators.IsEmpty); - OverloadResolution.GetStaticUserDefinedBinaryOperatorMethodNames(kind, checkOverflowAtRuntime, out string staticOperatorName1, out string? staticOperatorName2Opt); - BinaryOperatorAnalysisResult best = BinaryOperatorNonExtensionOverloadResolution(kind, isChecked: checkOverflowAtRuntime, staticOperatorName1, staticOperatorName2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators); + if (!best.HasValue && resultKind != LookupResultKind.Ambiguous) + { + Debug.Assert(resultKind is LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); + Debug.Assert(originalUserDefinedOperators.IsEmpty); - Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); - Debug.Assert(best.HasValue == (resultKind is LookupResultKind.Viable)); - Debug.Assert(resultKind is not (LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty) || originalUserDefinedOperators.IsEmpty); + LookupResultKind staticExtensionResultKind; + ImmutableArray staticExtensionOriginalUserDefinedOperators; + BinaryOperatorAnalysisResult? staticExtensionBest; + BoundCompoundAssignmentOperator? instanceExtensionResult = tryApplyUserDefinedExtensionOperator( + node, kind, tryInstance, checkOverflowAtRuntime, + staticOperatorName1, staticOperatorName2Opt, + checkedInstanceOperatorName, ordinaryInstanceOperatorName, + left, right, diagnostics, + out staticExtensionBest, out staticExtensionResultKind, out staticExtensionOriginalUserDefinedOperators, + ref operatorResolutionForReporting); - if (!best.HasValue && resultKind != LookupResultKind.Ambiguous) - { - Debug.Assert(resultKind is LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); - Debug.Assert(originalUserDefinedOperators.IsEmpty); + if (instanceExtensionResult is not null) + { + Debug.Assert(instanceExtensionResult.ResultKind is LookupResultKind.Viable || !instanceExtensionResult.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty); + return instanceExtensionResult; + } - LookupResultKind staticExtensionResultKind; - ImmutableArray staticExtensionOriginalUserDefinedOperators; - BinaryOperatorAnalysisResult? staticExtensionBest; - BoundCompoundAssignmentOperator? instanceExtensionResult = tryApplyUserDefinedExtensionOperator( - node, kind, tryInstance, checkOverflowAtRuntime, - staticOperatorName1, staticOperatorName2Opt, - checkedInstanceOperatorName, ordinaryInstanceOperatorName, - left, right, diagnostics, - out staticExtensionBest, out staticExtensionResultKind, out staticExtensionOriginalUserDefinedOperators); + if (staticExtensionBest.HasValue) + { + best = staticExtensionBest.GetValueOrDefault(); + resultKind = staticExtensionResultKind; + originalUserDefinedOperators = staticExtensionOriginalUserDefinedOperators; + } + } - if (instanceExtensionResult is not null) + if (!best.HasValue) { - Debug.Assert(instanceExtensionResult.ResultKind is LookupResultKind.Viable || !instanceExtensionResult.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty); - return instanceExtensionResult; + return createBadCompoundAssignmentOperator(node, kind, left, right, resultKind, originalUserDefinedOperators, ref operatorResolutionForReporting, diagnostics); } - if (staticExtensionBest.HasValue) + if (best.Signature.Method is { } bestMethod) { - best = staticExtensionBest.GetValueOrDefault(); - resultKind = staticExtensionResultKind; - originalUserDefinedOperators = staticExtensionOriginalUserDefinedOperators; + ReportObsoleteAndFeatureAvailabilityDiagnostics(bestMethod, node, diagnostics); + ReportUseSite(bestMethod, diagnostics, node); } - } - - if (!best.HasValue) - { - return createBadCompoundAssignmentOperator(node, kind, left, right, resultKind, originalUserDefinedOperators, diagnostics); - } - - if (best.Signature.Method is { } bestMethod) - { - ReportObsoleteAndFeatureAvailabilityDiagnostics(bestMethod, node, diagnostics); - ReportUseSite(bestMethod, diagnostics, node); - } - // The rules in the spec for determining additional errors are bit confusing. In particular - // this line is misleading: - // - // "for predefined operators ... x op= y is permitted if both x op y and x = y are permitted" - // - // That's not accurate in many cases. For example, "x += 1" is permitted if x is string or - // any enum type, but x = 1 is not legal for strings or enums. - // - // The correct rules are spelled out in the spec: - // - // Spec §7.17.2: - // An operation of the form x op= y is processed by applying binary operator overload - // resolution (§7.3.4) as if the operation was written x op y. - // Let R be the return type of the selected operator, and T the type of x. Then, - // - // * If an implicit conversion from an expression of type R to the type T exists, - // the operation is evaluated as x = (T)(x op y), except that x is evaluated only once. - // [no cast is inserted, unless the conversion is implicit dynamic] - // * Otherwise, if - // (1) the selected operator is a predefined operator, - // (2) if R is explicitly convertible to T, and - // (3.1) if y is implicitly convertible to T or - // (3.2) the operator is a shift operator... [then cast the result to T] - // * Otherwise ... a binding-time error occurs. - - // So let's tease that out. There are two possible errors: the conversion from the - // operator result type to the left hand type could be bad, and the conversion - // from the right hand side to the left hand type could be bad. - // - // We report the first error under the following circumstances: - // - // * The final conversion is bad, or - // * The final conversion is explicit and the selected operator is not predefined - // - // We report the second error under the following circumstances: - // - // * The final conversion is explicit, and - // * The selected operator is predefined, and - // * the selected operator is not a shift, and - // * the right-to-left conversion is not implicit + // The rules in the spec for determining additional errors are bit confusing. In particular + // this line is misleading: + // + // "for predefined operators ... x op= y is permitted if both x op y and x = y are permitted" + // + // That's not accurate in many cases. For example, "x += 1" is permitted if x is string or + // any enum type, but x = 1 is not legal for strings or enums. + // + // The correct rules are spelled out in the spec: + // + // Spec §7.17.2: + // An operation of the form x op= y is processed by applying binary operator overload + // resolution (§7.3.4) as if the operation was written x op y. + // Let R be the return type of the selected operator, and T the type of x. Then, + // + // * If an implicit conversion from an expression of type R to the type T exists, + // the operation is evaluated as x = (T)(x op y), except that x is evaluated only once. + // [no cast is inserted, unless the conversion is implicit dynamic] + // * Otherwise, if + // (1) the selected operator is a predefined operator, + // (2) if R is explicitly convertible to T, and + // (3.1) if y is implicitly convertible to T or + // (3.2) the operator is a shift operator... [then cast the result to T] + // * Otherwise ... a binding-time error occurs. + + // So let's tease that out. There are two possible errors: the conversion from the + // operator result type to the left hand type could be bad, and the conversion + // from the right hand side to the left hand type could be bad. + // + // We report the first error under the following circumstances: + // + // * The final conversion is bad, or + // * The final conversion is explicit and the selected operator is not predefined + // + // We report the second error under the following circumstances: + // + // * The final conversion is explicit, and + // * The selected operator is predefined, and + // * the selected operator is not a shift, and + // * the right-to-left conversion is not implicit - bool hasError = false; + bool hasError = false; - BinaryOperatorSignature bestSignature = best.Signature; + BinaryOperatorSignature bestSignature = best.Signature; - CheckNativeIntegerFeatureAvailability(bestSignature.Kind, node, diagnostics); - CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, bestSignature.Method, - isUnsignedRightShift: bestSignature.Kind.Operator() == BinaryOperatorKind.UnsignedRightShift, bestSignature.ConstrainedToTypeOpt, diagnostics); + CheckNativeIntegerFeatureAvailability(bestSignature.Kind, node, diagnostics); + CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, bestSignature.Method, + isUnsignedRightShift: bestSignature.Kind.Operator() == BinaryOperatorKind.UnsignedRightShift, bestSignature.ConstrainedToTypeOpt, diagnostics); - if (checkOverflowAtRuntime) - { - bestSignature = new BinaryOperatorSignature( - bestSignature.Kind.WithOverflowChecksIfApplicable(checkOverflowAtRuntime), - bestSignature.LeftType, - bestSignature.RightType, - bestSignature.ReturnType, - bestSignature.Method, - bestSignature.ConstrainedToTypeOpt); - } + if (checkOverflowAtRuntime) + { + bestSignature = new BinaryOperatorSignature( + bestSignature.Kind.WithOverflowChecksIfApplicable(checkOverflowAtRuntime), + bestSignature.LeftType, + bestSignature.RightType, + bestSignature.ReturnType, + bestSignature.Method, + bestSignature.ConstrainedToTypeOpt); + } - BoundExpression rightConverted = CreateConversion(right, best.RightConversion, bestSignature.RightType, diagnostics); + BoundExpression rightConverted = CreateConversion(right, best.RightConversion, bestSignature.RightType, diagnostics); - bool isPredefinedOperator = !bestSignature.Kind.IsUserDefined(); + bool isPredefinedOperator = !bestSignature.Kind.IsUserDefined(); - var leftType = left.Type; - Debug.Assert(leftType is { }); + var leftType = left.Type; + Debug.Assert(leftType is { }); - var finalPlaceholder = new BoundValuePlaceholder(node, bestSignature.ReturnType); + var finalPlaceholder = new BoundValuePlaceholder(node, bestSignature.ReturnType); - BoundExpression? finalConversion = GenerateConversionForAssignment(leftType, finalPlaceholder, diagnostics, - ConversionForAssignmentFlags.CompoundAssignment | - (isPredefinedOperator ? ConversionForAssignmentFlags.PredefinedOperator : ConversionForAssignmentFlags.None)); + BoundExpression? finalConversion = GenerateConversionForAssignment(leftType, finalPlaceholder, diagnostics, + ConversionForAssignmentFlags.CompoundAssignment | + (isPredefinedOperator ? ConversionForAssignmentFlags.PredefinedOperator : ConversionForAssignmentFlags.None)); - if (finalConversion.HasErrors) - { - hasError = true; - } + if (finalConversion.HasErrors) + { + hasError = true; + } - if (finalConversion is not BoundConversion final) - { - Debug.Assert(finalConversion.HasErrors || (object)finalConversion == finalPlaceholder); - if ((object)finalConversion != finalPlaceholder) + if (finalConversion is not BoundConversion final) { - finalPlaceholder = null; - finalConversion = null; + Debug.Assert(finalConversion.HasErrors || (object)finalConversion == finalPlaceholder); + if ((object)finalConversion != finalPlaceholder) + { + finalPlaceholder = null; + finalConversion = null; + } } - } - else if (final.Conversion.IsExplicit && - isPredefinedOperator && - !kind.IsShift()) - { - Conversion rightToLeftConversion = this.Conversions.ClassifyConversionFromExpression(right, leftType, isChecked: checkOverflowAtRuntime, ref useSiteInfo); - if (!rightToLeftConversion.IsImplicit || !rightToLeftConversion.IsValid) + else if (final.Conversion.IsExplicit && + isPredefinedOperator && + !kind.IsShift()) { - hasError = true; - GenerateImplicitConversionError(diagnostics, node, rightToLeftConversion, right, leftType); + Conversion rightToLeftConversion = this.Conversions.ClassifyConversionFromExpression(right, leftType, isChecked: checkOverflowAtRuntime, ref useSiteInfo); + if (!rightToLeftConversion.IsImplicit || !rightToLeftConversion.IsValid) + { + hasError = true; + GenerateImplicitConversionError(diagnostics, node, rightToLeftConversion, right, leftType); + } } - } - diagnostics.Add(node, useSiteInfo); + diagnostics.Add(node, useSiteInfo); - if (!hasError && !bestSignature.Kind.IsUserDefined() && leftType.IsVoidPointer()) - { - Error(diagnostics, ErrorCode.ERR_VoidError, node); - hasError = true; - } + if (!hasError && !bestSignature.Kind.IsUserDefined() && leftType.IsVoidPointer()) + { + Error(diagnostics, ErrorCode.ERR_VoidError, node); + hasError = true; + } - // Any events that weren't handled above (by BindEventAssignment) are bad - we just followed this - // code path for the diagnostics. Make sure we don't report success. - Debug.Assert(left.Kind != BoundKind.EventAccess || hasError); + // Any events that weren't handled above (by BindEventAssignment) are bad - we just followed this + // code path for the diagnostics. Make sure we don't report success. + Debug.Assert(left.Kind != BoundKind.EventAccess || hasError); - var leftPlaceholder = new BoundValuePlaceholder(left.Syntax, leftType).MakeCompilerGenerated(); - var leftConversion = CreateConversion(node.Left, leftPlaceholder, best.LeftConversion, isCast: false, conversionGroupOpt: null, best.Signature.LeftType, diagnostics); + var leftPlaceholder = new BoundValuePlaceholder(left.Syntax, leftType).MakeCompilerGenerated(); + var leftConversion = CreateConversion(node.Left, leftPlaceholder, best.LeftConversion, isCast: false, conversionGroupOpt: null, best.Signature.LeftType, diagnostics); - return new BoundCompoundAssignmentOperator(node, bestSignature, left, rightConverted, - leftPlaceholder, leftConversion, finalPlaceholder, finalConversion, resultKind, originalUserDefinedOperators, leftType, hasError); + return new BoundCompoundAssignmentOperator(node, bestSignature, left, rightConverted, + leftPlaceholder, leftConversion, finalPlaceholder, finalConversion, resultKind, originalUserDefinedOperators, leftType, hasError); + } BoundCompoundAssignmentOperator createBadCompoundAssignmentOperator( AssignmentExpressionSyntax node, @@ -319,9 +329,10 @@ BoundCompoundAssignmentOperator createBadCompoundAssignmentOperator( BoundExpression right, LookupResultKind resultKind, ImmutableArray originalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { - ReportAssignmentOperatorError(node, kind, diagnostics, left, right, resultKind); + ReportAssignmentOperatorError(node, kind, diagnostics, left, right, resultKind, ref operatorResolutionForReporting); left = BindToTypeForErrorRecovery(left); right = BindToTypeForErrorRecovery(right); return new BoundCompoundAssignmentOperator(node, BinaryOperatorSignature.Error, left, right, @@ -370,6 +381,7 @@ bool shouldTryUserDefinedInstanceOperator(AssignmentExpressionSyntax node, bool string ordinaryName, BoundExpression left, BoundExpression right, + ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { var leftType = left.Type; @@ -399,7 +411,7 @@ bool shouldTryUserDefinedInstanceOperator(AssignmentExpressionSyntax node, bool AnalyzedArguments? analyzedArguments = null; - BoundCompoundAssignmentOperator? inPlaceResult = tryInstanceOperatorOverloadResolutionAndFreeMethods(node, kind, checkOverflowAtRuntime, isExtension: false, left, right, ref analyzedArguments, methods, diagnostics); + BoundCompoundAssignmentOperator? inPlaceResult = tryInstanceOperatorOverloadResolutionAndFreeMethods(node, kind, checkOverflowAtRuntime, isExtension: false, left, right, ref analyzedArguments, methods, ref operatorResolutionForReporting, diagnostics); Debug.Assert(analyzedArguments is not null); analyzedArguments.Free(); @@ -416,6 +428,7 @@ bool shouldTryUserDefinedInstanceOperator(AssignmentExpressionSyntax node, bool BoundExpression right, ref AnalyzedArguments? analyzedArguments, ArrayBuilder methods, + ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { Debug.Assert(!methods.IsEmpty); @@ -540,7 +553,11 @@ bool shouldTryUserDefinedInstanceOperator(AssignmentExpressionSyntax node, bool methods.Free(); } - overloadResolutionResult.Free(); + if (!operatorResolutionForReporting.SaveResult(overloadResolutionResult, isExtension)) + { + overloadResolutionResult.Free(); + } + return inPlaceResult; } @@ -566,13 +583,14 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol leftType, BindingDiag BindingDiagnosticBag diagnostics, out BinaryOperatorAnalysisResult? staticBest, out LookupResultKind staticResultKind, - out ImmutableArray staticOriginalUserDefinedOperators) + out ImmutableArray staticOriginalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting) { staticBest = null; staticResultKind = LookupResultKind.Empty; staticOriginalUserDefinedOperators = []; - var result = BinaryOperatorOverloadResolutionResult.GetInstance(); + BinaryOperatorOverloadResolutionResult? result = BinaryOperatorOverloadResolutionResult.GetInstance(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var extensionCandidatesInSingleScope = ArrayBuilder.GetInstance(); BoundCompoundAssignmentOperator? inPlaceResult = null; @@ -593,7 +611,7 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol leftType, BindingDiag inPlaceResult = tryApplyUserDefinedInstanceExtensionOperatorInSingleScope( node, extensionCandidatesInSingleScope, kind, checkOverflowAtRuntime, checkedInstanceOperatorName, ordinaryInstanceOperatorName, - left, right, ref analyzedArguments, diagnostics); + left, right, ref analyzedArguments, ref operatorResolutionForReporting, diagnostics); if (inPlaceResult is not null) { break; @@ -611,15 +629,25 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol leftType, BindingDiag left, right, result, ref useSiteInfo)) { staticBest = BinaryOperatorAnalyzeOverloadResolutionResult(result, out staticResultKind, out staticOriginalUserDefinedOperators); + if (operatorResolutionForReporting.SaveResult(result, isExtension: true)) + { + result = null; + } + break; } + + if (operatorResolutionForReporting.SaveResult(result, isExtension: true)) + { + result = BinaryOperatorOverloadResolutionResult.GetInstance(); + } } diagnostics.Add(node, useSiteInfo); analyzedArguments?.Free(); extensionCandidatesInSingleScope.Free(); - result.Free(); + result?.Free(); return inPlaceResult; } @@ -633,6 +661,7 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol leftType, BindingDiag BoundExpression left, BoundExpression right, ref AnalyzedArguments? analyzedArguments, + ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { ArrayBuilder? methods = LookupUserDefinedInstanceExtensionOperatorsInSingleScope( @@ -647,7 +676,7 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol leftType, BindingDiag return null; } - return tryInstanceOperatorOverloadResolutionAndFreeMethods(node, kind, checkOverflowAtRuntime, isExtension: true, left, right, ref analyzedArguments, methods, diagnostics); + return tryInstanceOperatorOverloadResolutionAndFreeMethods(node, kind, checkOverflowAtRuntime, isExtension: true, left, right, ref analyzedArguments, methods, ref operatorResolutionForReporting, diagnostics); } } @@ -984,18 +1013,22 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Bi ImmutableArray originalUserDefinedOperators; BinaryOperatorSignature signature; BinaryOperatorAnalysisResult best; + OperatorResolutionForReporting operatorResolutionForReporting = default; + bool foundOperator = BindSimpleBinaryOperatorParts(node, diagnostics, left, right, kind, - out resultKind, out originalUserDefinedOperators, out signature, out best); + out resultKind, out originalUserDefinedOperators, out signature, out best, ref operatorResolutionForReporting); BinaryOperatorKind resultOperatorKind = signature.Kind; bool hasErrors = false; if (!foundOperator) { - ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, resultKind); + ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, resultKind, ref operatorResolutionForReporting); resultOperatorKind &= ~BinaryOperatorKind.TypeMask; hasErrors = true; } + operatorResolutionForReporting.Free(); + switch (node.Kind()) { case SyntaxKind.EqualsExpression: @@ -1104,7 +1137,7 @@ resultOperatorKind is BinaryOperatorKind.ObjectEqual or BinaryOperatorKind.Objec private bool BindSimpleBinaryOperatorParts(BinaryExpressionSyntax node, BindingDiagnosticBag diagnostics, BoundExpression left, BoundExpression right, BinaryOperatorKind kind, out LookupResultKind resultKind, out ImmutableArray originalUserDefinedOperators, - out BinaryOperatorSignature resultSignature, out BinaryOperatorAnalysisResult best) + out BinaryOperatorSignature resultSignature, out BinaryOperatorAnalysisResult best, ref OperatorResolutionForReporting operatorResolutionForReporting) { if (!IsTypelessExpressionAllowedInBinaryOperator(kind, left, right)) { @@ -1117,9 +1150,9 @@ private bool BindSimpleBinaryOperatorParts(BinaryExpressionSyntax node, BindingD bool isChecked = CheckOverflowAtRuntime; OverloadResolution.GetStaticUserDefinedBinaryOperatorMethodNames(kind, isChecked, out string name1, out string name2Opt); - best = this.BinaryOperatorOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators); + best = this.BinaryOperatorOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); - return bindSimpleBinaryOperatorPartsContinue(node, diagnostics, left, right, kind, ref resultKind, ref originalUserDefinedOperators, out resultSignature, ref best, isChecked, name1, name2Opt); + return bindSimpleBinaryOperatorPartsContinue(node, diagnostics, left, right, kind, ref resultKind, ref originalUserDefinedOperators, out resultSignature, ref best, isChecked, name1, name2Opt, ref operatorResolutionForReporting); bool bindSimpleBinaryOperatorPartsContinue( BinaryExpressionSyntax node, @@ -1133,7 +1166,8 @@ bool bindSimpleBinaryOperatorPartsContinue( ref BinaryOperatorAnalysisResult best, bool isChecked, string name1, - string name2Opt) + string name2Opt, + ref OperatorResolutionForReporting operatorResolutionForReporting) { // However, as an implementation detail, we never "fail to find an applicable // operator" during overload resolution if we have x == null, x == default, etc. We always @@ -1193,17 +1227,21 @@ bool bindSimpleBinaryOperatorPartsContinue( // Try extension operators since predefined object equality was not applicable LookupResultKind extensionResultKind; ImmutableArray extensionOriginalUserDefinedOperators; - BinaryOperatorAnalysisResult? extensionBest = BinaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out extensionResultKind, out extensionOriginalUserDefinedOperators); + BinaryOperatorAnalysisResult? extensionBest = BinaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, + out extensionResultKind, out extensionOriginalUserDefinedOperators, ref operatorResolutionForReporting); if (extensionBest.HasValue) { best = extensionBest.GetValueOrDefault(); resultKind = extensionResultKind; originalUserDefinedOperators = extensionOriginalUserDefinedOperators; - foundOperator = bindSimpleBinaryOperatorPartsContinue(node, diagnostics, left, right, kind, ref resultKind, ref originalUserDefinedOperators, out resultSignature, ref best, isChecked, name1, name2Opt); + foundOperator = bindSimpleBinaryOperatorPartsContinue(node, diagnostics, left, right, kind, ref resultKind, ref originalUserDefinedOperators, out resultSignature, ref best, isChecked, name1, name2Opt, ref operatorResolutionForReporting); } + + operatorResolutionForReporting.Free(); } } + return foundOperator; } } @@ -1252,9 +1290,8 @@ BoundExpression doRebind(BindingDiagnosticBag diagnostics, BoundBinaryOperator? return left; } } -#nullable disable - private static void ReportUnaryOperatorError(CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, string operatorName, BoundExpression operand, LookupResultKind resultKind) + private void ReportUnaryOperatorError(CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, string operatorName, BoundExpression operand, LookupResultKind resultKind, ref OperatorResolutionForReporting operatorResolutionForReporting) { if (operand.IsLiteralDefault()) { @@ -1262,6 +1299,11 @@ private static void ReportUnaryOperatorError(CSharpSyntaxNode node, BindingDiagn return; } + if (operatorResolutionForReporting.TryReportDiagnostics(node, diagnostics, this, operand.Display, null)) + { + return; + } + ErrorCode errorCode = resultKind == LookupResultKind.Ambiguous ? ErrorCode.ERR_AmbigUnaryOp : // Operator '{0}' is ambiguous on an operand of type '{1}' ErrorCode.ERR_BadUnaryOp; // Operator '{0}' cannot be applied to operand of type '{1}' @@ -1269,11 +1311,12 @@ private static void ReportUnaryOperatorError(CSharpSyntaxNode node, BindingDiagn Error(diagnostics, errorCode, node, operatorName, operand.Display); } - private void ReportAssignmentOperatorError(AssignmentExpressionSyntax node, BinaryOperatorKind kind, BindingDiagnosticBag diagnostics, BoundExpression left, BoundExpression right, LookupResultKind resultKind) + private void ReportAssignmentOperatorError(AssignmentExpressionSyntax node, BinaryOperatorKind kind, BindingDiagnosticBag diagnostics, BoundExpression left, BoundExpression right, + LookupResultKind resultKind, ref OperatorResolutionForReporting operatorResolutionForReporting) { if (IsTypelessExpressionAllowedInBinaryOperator(kind, left, right) && node.OperatorToken.RawKind is (int)SyntaxKind.PlusEqualsToken or (int)SyntaxKind.MinusEqualsToken && - (object)left.Type != null && left.Type.TypeKind == TypeKind.Delegate) + (object?)left.Type != null && left.Type.TypeKind == TypeKind.Delegate) { // Special diagnostic for delegate += and -= about wrong right-hand-side var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; @@ -1284,12 +1327,18 @@ node.OperatorToken.RawKind is (int)SyntaxKind.PlusEqualsToken or (int)SyntaxKind } else { - ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, resultKind); + ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, resultKind, ref operatorResolutionForReporting); } } - private void ReportBinaryOperatorError(ExpressionSyntax node, BindingDiagnosticBag diagnostics, SyntaxToken operatorToken, BoundExpression left, BoundExpression right, LookupResultKind resultKind) + private void ReportBinaryOperatorError(ExpressionSyntax node, BindingDiagnosticBag diagnostics, SyntaxToken operatorToken, BoundExpression left, BoundExpression right, + LookupResultKind resultKind, ref OperatorResolutionForReporting operatorResolutionForReporting) { + if (operatorResolutionForReporting.TryReportDiagnostics(node, diagnostics, this, left.Display, right.Display)) + { + return; + } + bool isEquality = operatorToken.Kind() == SyntaxKind.EqualsEqualsToken || operatorToken.Kind() == SyntaxKind.ExclamationEqualsToken; switch (left.Kind, right.Kind) { @@ -1317,7 +1366,6 @@ private void ReportBinaryOperatorError(ExpressionSyntax node, BindingDiagnosticB return; } -#nullable enable ErrorCode errorCode; switch (resultKind) @@ -1385,161 +1433,169 @@ private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax no private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax node, BoundExpression left, BoundExpression right, BindingDiagnosticBag diagnostics) { - BinaryOperatorKind kind = SyntaxKindToBinaryOperatorKind(node.Kind()); - - Debug.Assert(kind == BinaryOperatorKind.LogicalAnd || kind == BinaryOperatorKind.LogicalOr); - - // Let's take an easy out here. The vast majority of the time the operands will - // both be bool. This is the only situation in which the expression can be a - // constant expression, so do the folding now if we can. + OperatorResolutionForReporting operatorResolutionForReporting = default; + BoundExpression result = bindConditionalLogicalOperator(node, left, right, diagnostics, ref operatorResolutionForReporting); + operatorResolutionForReporting.Free(); + return result; - if ((object)left.Type != null && left.Type.SpecialType == SpecialType.System_Boolean && - (object)right.Type != null && right.Type.SpecialType == SpecialType.System_Boolean) + BoundExpression bindConditionalLogicalOperator(BinaryExpressionSyntax node, BoundExpression left, BoundExpression right, BindingDiagnosticBag diagnostics, ref OperatorResolutionForReporting operatorResolutionForReporting) { - var constantValue = FoldBinaryOperator(node, kind | BinaryOperatorKind.Bool, left, right, left.Type, diagnostics); + BinaryOperatorKind kind = SyntaxKindToBinaryOperatorKind(node.Kind()); - // NOTE: no candidate user-defined operators. - return new BoundBinaryOperator(node, kind | BinaryOperatorKind.Bool, constantValue, methodOpt: null, constrainedToTypeOpt: null, - resultKind: LookupResultKind.Viable, left, right, type: left.Type, hasErrors: constantValue != null && constantValue.IsBad); - } + Debug.Assert(kind == BinaryOperatorKind.LogicalAnd || kind == BinaryOperatorKind.LogicalOr); - // If either operand is bad, don't try to do binary operator overload resolution; that will just - // make cascading errors. + // Let's take an easy out here. The vast majority of the time the operands will + // both be bool. This is the only situation in which the expression can be a + // constant expression, so do the folding now if we can. - if (left.HasAnyErrors || right.HasAnyErrors) - { - // NOTE: no candidate user-defined operators. - return new BoundBinaryOperator(node, kind, ConstantValue.NotAvailable, methodOpt: null, constrainedToTypeOpt: null, - resultKind: LookupResultKind.Empty, left, right, type: GetBinaryOperatorErrorType(kind, diagnostics, node), hasErrors: true); - } + if ((object)left.Type != null && left.Type.SpecialType == SpecialType.System_Boolean && + (object)right.Type != null && right.Type.SpecialType == SpecialType.System_Boolean) + { + var constantValue = FoldBinaryOperator(node, kind | BinaryOperatorKind.Bool, left, right, left.Type, diagnostics); - if (left.HasDynamicType() || right.HasDynamicType()) - { - left = BindToNaturalType(left, diagnostics); - right = BindToNaturalType(right, diagnostics); - return BindDynamicBinaryOperator(node, kind, left, right, diagnostics); - } + // NOTE: no candidate user-defined operators. + return new BoundBinaryOperator(node, kind | BinaryOperatorKind.Bool, constantValue, methodOpt: null, constrainedToTypeOpt: null, + resultKind: LookupResultKind.Viable, left, right, type: left.Type, hasErrors: constantValue != null && constantValue.IsBad); + } - LookupResultKind lookupResult; - ImmutableArray originalUserDefinedOperators; - BinaryOperatorAnalysisResult best; + // If either operand is bad, don't try to do binary operator overload resolution; that will just + // make cascading errors. - if (!IsTypelessExpressionAllowedInBinaryOperator(kind, left, right)) - { - lookupResult = LookupResultKind.OverloadResolutionFailure; - originalUserDefinedOperators = default(ImmutableArray); - best = default(BinaryOperatorAnalysisResult); - } - else - { - best = this.BinaryOperatorOverloadResolution(kind, isChecked: CheckOverflowAtRuntime, left, right, node, diagnostics, out lookupResult, out originalUserDefinedOperators); - } + if (left.HasAnyErrors || right.HasAnyErrors) + { + // NOTE: no candidate user-defined operators. + return new BoundBinaryOperator(node, kind, ConstantValue.NotAvailable, methodOpt: null, constrainedToTypeOpt: null, + resultKind: LookupResultKind.Empty, left, right, type: GetBinaryOperatorErrorType(kind, diagnostics, node), hasErrors: true); + } - // SPEC: If overload resolution fails to find a single best operator, or if overload - // SPEC: resolution selects one of the predefined integer logical operators, a binding- - // SPEC: time error occurs. - // - // SPEC OMISSION: We should probably clarify that the enum logical operators count as - // SPEC OMISSION: integer logical operators. Basically the rule here should actually be: - // SPEC OMISSION: if overload resolution selects something other than a user-defined - // SPEC OMISSION: operator or the built in not-lifted operator on bool, an error occurs. - // + if (left.HasDynamicType() || right.HasDynamicType()) + { + left = BindToNaturalType(left, diagnostics); + right = BindToNaturalType(right, diagnostics); + return BindDynamicBinaryOperator(node, kind, left, right, diagnostics); + } - if (!best.HasValue) - { - ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, lookupResult); - } - else - { - // There are two non-error possibilities. Either both operands are implicitly convertible to - // bool, or we've got a valid user-defined operator. - BinaryOperatorSignature signature = best.Signature; + LookupResultKind lookupResult; + ImmutableArray originalUserDefinedOperators; + BinaryOperatorAnalysisResult best; - if (signature.Method is { } bestMethod) + if (!IsTypelessExpressionAllowedInBinaryOperator(kind, left, right)) { - ReportObsoleteAndFeatureAvailabilityDiagnostics(bestMethod, node, diagnostics); - ReportUseSite(bestMethod, diagnostics, node); + lookupResult = LookupResultKind.OverloadResolutionFailure; + originalUserDefinedOperators = default(ImmutableArray); + best = default(BinaryOperatorAnalysisResult); + } + else + { + best = this.BinaryOperatorOverloadResolution(kind, isChecked: CheckOverflowAtRuntime, left, right, node, diagnostics, out lookupResult, out originalUserDefinedOperators, ref operatorResolutionForReporting); } - bool bothBool = signature.LeftType.SpecialType == SpecialType.System_Boolean && - signature.RightType.SpecialType == SpecialType.System_Boolean; - - UnaryOperatorAnalysisResult? trueOperator = null, falseOperator = null; + // SPEC: If overload resolution fails to find a single best operator, or if overload + // SPEC: resolution selects one of the predefined integer logical operators, a binding- + // SPEC: time error occurs. + // + // SPEC OMISSION: We should probably clarify that the enum logical operators count as + // SPEC OMISSION: integer logical operators. Basically the rule here should actually be: + // SPEC OMISSION: if overload resolution selects something other than a user-defined + // SPEC OMISSION: operator or the built in not-lifted operator on bool, an error occurs. + // - if (!bothBool && !signature.Kind.IsUserDefined()) + if (!best.HasValue) { - ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, lookupResult); + ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, lookupResult, ref operatorResolutionForReporting); } - else if (bothBool || IsValidUserDefinedConditionalLogicalOperator(node, signature, diagnostics, out trueOperator, out falseOperator)) + else { - var resultLeft = CreateConversion(left, best.LeftConversion, signature.LeftType, diagnostics); - var resultRight = CreateConversion(right, best.RightConversion, signature.RightType, diagnostics); - var resultKind = kind | signature.Kind.OperandTypes(); - if (signature.Kind.IsLifted()) - { - resultKind |= BinaryOperatorKind.Lifted; - } + // There are two non-error possibilities. Either both operands are implicitly convertible to + // bool, or we've got a valid user-defined operator. + BinaryOperatorSignature signature = best.Signature; - if (resultKind.IsUserDefined()) + if (signature.Method is { } bestMethod) { - Debug.Assert(trueOperator is { HasValue: true }); - Debug.Assert(falseOperator is { HasValue: true }); - - UnaryOperatorAnalysisResult trueFalseOperator = (kind == BinaryOperatorKind.LogicalAnd ? falseOperator : trueOperator).GetValueOrDefault(); - - _ = CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, signature.Method, isUnsignedRightShift: false, signature.ConstrainedToTypeOpt, diagnostics) && - CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, trueFalseOperator.Signature.Method, - isUnsignedRightShift: false, signature.ConstrainedToTypeOpt, diagnostics); + ReportObsoleteAndFeatureAvailabilityDiagnostics(bestMethod, node, diagnostics); + ReportUseSite(bestMethod, diagnostics, node); + } - Debug.Assert(resultLeft.Type.Equals(signature.LeftType, TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); - var operandPlaceholder = new BoundValuePlaceholder(resultLeft.Syntax, resultLeft.Type).MakeCompilerGenerated(); + bool bothBool = signature.LeftType.SpecialType == SpecialType.System_Boolean && + signature.RightType.SpecialType == SpecialType.System_Boolean; - BoundExpression operandConversion = CreateConversion( - resultLeft.Syntax, - operandPlaceholder, - trueFalseOperator.Conversion, - isCast: false, - conversionGroupOpt: null, - trueFalseOperator.Signature.OperandType, - diagnostics); + UnaryOperatorAnalysisResult? trueOperator = null, falseOperator = null; - return new BoundUserDefinedConditionalLogicalOperator( - node, - resultKind, - signature.Method, - trueOperator.GetValueOrDefault().Signature.Method, - falseOperator.GetValueOrDefault().Signature.Method, - operandPlaceholder, - operandConversion, - signature.ConstrainedToTypeOpt, - lookupResult, - originalUserDefinedOperators, - resultLeft, - resultRight, - signature.ReturnType); + if (!bothBool && !signature.Kind.IsUserDefined()) + { + ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, lookupResult, ref operatorResolutionForReporting); } - else + else if (bothBool || IsValidUserDefinedConditionalLogicalOperator(node, signature, diagnostics, out trueOperator, out falseOperator)) { - Debug.Assert(bothBool); - Debug.Assert(!(signature.Method?.ContainingType?.IsInterface ?? false)); + var resultLeft = CreateConversion(left, best.LeftConversion, signature.LeftType, diagnostics); + var resultRight = CreateConversion(right, best.RightConversion, signature.RightType, diagnostics); + var resultKind = kind | signature.Kind.OperandTypes(); + if (signature.Kind.IsLifted()) + { + resultKind |= BinaryOperatorKind.Lifted; + } - return new BoundBinaryOperator( - node, - resultKind, - resultLeft, - resultRight, - ConstantValue.NotAvailable, - signature.Method, - signature.ConstrainedToTypeOpt, - lookupResult, - originalUserDefinedOperators, - signature.ReturnType); + if (resultKind.IsUserDefined()) + { + Debug.Assert(trueOperator is { HasValue: true }); + Debug.Assert(falseOperator is { HasValue: true }); + + UnaryOperatorAnalysisResult trueFalseOperator = (kind == BinaryOperatorKind.LogicalAnd ? falseOperator : trueOperator).GetValueOrDefault(); + + _ = CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, signature.Method, isUnsignedRightShift: false, signature.ConstrainedToTypeOpt, diagnostics) && + CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, trueFalseOperator.Signature.Method, + isUnsignedRightShift: false, signature.ConstrainedToTypeOpt, diagnostics); + + Debug.Assert(resultLeft.Type.Equals(signature.LeftType, TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); + var operandPlaceholder = new BoundValuePlaceholder(resultLeft.Syntax, resultLeft.Type).MakeCompilerGenerated(); + + BoundExpression operandConversion = CreateConversion( + resultLeft.Syntax, + operandPlaceholder, + trueFalseOperator.Conversion, + isCast: false, + conversionGroupOpt: null, + trueFalseOperator.Signature.OperandType, + diagnostics); + + return new BoundUserDefinedConditionalLogicalOperator( + node, + resultKind, + signature.Method, + trueOperator.GetValueOrDefault().Signature.Method, + falseOperator.GetValueOrDefault().Signature.Method, + operandPlaceholder, + operandConversion, + signature.ConstrainedToTypeOpt, + lookupResult, + originalUserDefinedOperators, + resultLeft, + resultRight, + signature.ReturnType); + } + else + { + Debug.Assert(bothBool); + Debug.Assert(!(signature.Method?.ContainingType?.IsInterface ?? false)); + + return new BoundBinaryOperator( + node, + resultKind, + resultLeft, + resultRight, + ConstantValue.NotAvailable, + signature.Method, + signature.ConstrainedToTypeOpt, + lookupResult, + originalUserDefinedOperators, + signature.ReturnType); + } } } - } - // We've already reported the error. - return new BoundBinaryOperator(node, kind, left, right, ConstantValue.NotAvailable, methodOpt: null, constrainedToTypeOpt: null, lookupResult, originalUserDefinedOperators, CreateErrorType(), true); + // We've already reported the error. + return new BoundBinaryOperator(node, kind, left, right, ConstantValue.NotAvailable, methodOpt: null, constrainedToTypeOpt: null, lookupResult, originalUserDefinedOperators, CreateErrorType(), true); + } } private bool IsValidDynamicCondition(BoundExpression left, bool isNegative, BindingDiagnosticBag diagnostics, out MethodSymbol userDefinedOperator) @@ -2012,11 +2068,12 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators) + out ImmutableArray originalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting) { OverloadResolution.GetStaticUserDefinedBinaryOperatorMethodNames(kind, isChecked, out string name1, out string name2Opt); - return BinaryOperatorOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators); + return BinaryOperatorOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); } private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( @@ -2029,9 +2086,10 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators) + out ImmutableArray originalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting) { - BinaryOperatorAnalysisResult possiblyBest = BinaryOperatorNonExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators); + BinaryOperatorAnalysisResult possiblyBest = BinaryOperatorNonExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); Debug.Assert(possiblyBest.HasValue == (resultKind is LookupResultKind.Viable)); @@ -2044,7 +2102,7 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( LookupResultKind extensionResultKind; ImmutableArray extensionOriginalUserDefinedOperators; - BinaryOperatorAnalysisResult? extensionBest = BinaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out extensionResultKind, out extensionOriginalUserDefinedOperators); + BinaryOperatorAnalysisResult? extensionBest = BinaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out extensionResultKind, out extensionOriginalUserDefinedOperators, ref operatorResolutionForReporting); if (extensionBest.HasValue) { @@ -2069,7 +2127,8 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators) + out ImmutableArray originalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting) { resultKind = LookupResultKind.Empty; originalUserDefinedOperators = []; @@ -2079,7 +2138,7 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( return null; } - var result = BinaryOperatorOverloadResolutionResult.GetInstance(); + BinaryOperatorOverloadResolutionResult? result = BinaryOperatorOverloadResolutionResult.GetInstance(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var extensionCandidatesInSingleScope = ArrayBuilder.GetInstance(); BinaryOperatorAnalysisResult? possiblyBest = null; @@ -2094,14 +2153,25 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( if (this.OverloadResolution.BinaryOperatorExtensionOverloadResolutionInSingleScope(extensionCandidatesInSingleScope, kind, isChecked, name1, name2Opt, left, right, result, ref useSiteInfo)) { possiblyBest = BinaryOperatorAnalyzeOverloadResolutionResult(result, out resultKind, out originalUserDefinedOperators); + + if (operatorResolutionForReporting.SaveResult(result, isExtension: true)) + { + result = null; + } + break; } + + if (operatorResolutionForReporting.SaveResult(result, isExtension: true)) + { + result = BinaryOperatorOverloadResolutionResult.GetInstance(); + } } diagnostics.Add(node, useSiteInfo); extensionCandidatesInSingleScope.Free(); - result.Free(); + result?.Free(); return possiblyBest; } @@ -2117,7 +2187,8 @@ private BinaryOperatorAnalysisResult BinaryOperatorNonExtensionOverloadResolutio CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators) + out ImmutableArray originalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting) { var result = BinaryOperatorOverloadResolutionResult.GetInstance(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); @@ -2125,7 +2196,10 @@ private BinaryOperatorAnalysisResult BinaryOperatorNonExtensionOverloadResolutio diagnostics.Add(node, useSiteInfo); var possiblyBest = BinaryOperatorAnalyzeOverloadResolutionResult(result, out resultKind, out originalUserDefinedOperators); - result.Free(); + if (!operatorResolutionForReporting.SaveResult(result, isExtension: false)) + { + result.Free(); + } Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); Debug.Assert(possiblyBest.HasValue == (resultKind is LookupResultKind.Viable)); @@ -2215,12 +2289,13 @@ private UnaryOperatorAnalysisResult UnaryOperatorOverloadResolution( CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators) + out ImmutableArray originalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting) { bool isChecked = CheckOverflowAtRuntime; OverloadResolution.GetStaticUserDefinedUnaryOperatorMethodNames(kind, isChecked, out string name1, out string name2Opt); - var best = UnaryOperatorNonExtensionOverloadResolution(kind, isChecked, name1, name2Opt, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators); + var best = UnaryOperatorNonExtensionOverloadResolution(kind, isChecked, name1, name2Opt, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); Debug.Assert(best.HasValue == (resultKind is LookupResultKind.Viable)); @@ -2233,7 +2308,8 @@ private UnaryOperatorAnalysisResult UnaryOperatorOverloadResolution( LookupResultKind extensionResultKind; ImmutableArray extensionOriginalUserDefinedOperators; - UnaryOperatorAnalysisResult? extensionBest = this.UnaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, operand, node, diagnostics, out extensionResultKind, out extensionOriginalUserDefinedOperators); + UnaryOperatorAnalysisResult? extensionBest = this.UnaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, operand, node, diagnostics, + out extensionResultKind, out extensionOriginalUserDefinedOperators, ref operatorResolutionForReporting); if (extensionBest.HasValue) { @@ -2255,7 +2331,8 @@ private UnaryOperatorAnalysisResult UnaryOperatorNonExtensionOverloadResolution( CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators) + out ImmutableArray originalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting) { var result = UnaryOperatorOverloadResolutionResult.GetInstance(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); @@ -2264,7 +2341,10 @@ private UnaryOperatorAnalysisResult UnaryOperatorNonExtensionOverloadResolution( UnaryOperatorAnalysisResult possiblyBest = AnalyzeUnaryOperatorOverloadResolutionResult(result, kind, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators); - result.Free(); + if (!operatorResolutionForReporting.SaveResult(result, isExtension: false)) + { + result.Free(); + } Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); Debug.Assert(possiblyBest.HasValue == (resultKind is LookupResultKind.Viable)); @@ -2358,7 +2438,8 @@ static bool isNuint(TypeSymbol type) CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators) + out ImmutableArray originalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting) { resultKind = LookupResultKind.Empty; originalUserDefinedOperators = []; @@ -2369,7 +2450,7 @@ static bool isNuint(TypeSymbol type) return null; } - var result = UnaryOperatorOverloadResolutionResult.GetInstance(); + UnaryOperatorOverloadResolutionResult? result = UnaryOperatorOverloadResolutionResult.GetInstance(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var extensionCandidatesInSingleScope = ArrayBuilder.GetInstance(); UnaryOperatorAnalysisResult? possiblyBest = null; @@ -2384,14 +2465,25 @@ static bool isNuint(TypeSymbol type) if (this.OverloadResolution.UnaryOperatorExtensionOverloadResolutionInSingleScope(extensionCandidatesInSingleScope, kind, isChecked, name1, name2Opt, operand, result, ref useSiteInfo)) { possiblyBest = AnalyzeUnaryOperatorOverloadResolutionResult(result, kind, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators); + + if (operatorResolutionForReporting.SaveResult(result, isExtension: true)) + { + result = null; + } + break; } + + if (operatorResolutionForReporting.SaveResult(result, isExtension: true)) + { + result = UnaryOperatorOverloadResolutionResult.GetInstance(); + } } diagnostics.Add(node, useSiteInfo); extensionCandidatesInSingleScope.Free(); - result.Free(); + result?.Free(); return possiblyBest; } @@ -3181,172 +3273,181 @@ private enum InstanceUserDefinedIncrementUsageMode : byte private BoundExpression BindIncrementOperator(ExpressionSyntax node, ExpressionSyntax operandSyntax, SyntaxToken operatorToken, BindingDiagnosticBag diagnostics) { - operandSyntax.CheckDeconstructionCompatibleArgument(diagnostics); - - BoundExpression operand = BindToNaturalType(BindValue(operandSyntax, diagnostics, BindValueKind.IncrementDecrement), diagnostics); - UnaryOperatorKind kind = SyntaxKindToUnaryOperatorKind(node.Kind()); + OperatorResolutionForReporting operatorResolutionForReporting = default; + BoundExpression result = bindIncrementOperator(node, operandSyntax, operatorToken, diagnostics, ref operatorResolutionForReporting); + operatorResolutionForReporting.Free(); + return result; - // If the operand is bad, avoid generating cascading errors. - if (operand.HasAnyErrors) + BoundExpression bindIncrementOperator(ExpressionSyntax node, ExpressionSyntax operandSyntax, SyntaxToken operatorToken, BindingDiagnosticBag diagnostics, ref OperatorResolutionForReporting operatorResolutionForReporting) { - // NOTE: no candidate user-defined operators. - return new BoundIncrementOperator( - node, - kind, - operand, - methodOpt: null, - constrainedToTypeOpt: null, - operandPlaceholder: null, - operandConversion: null, - resultPlaceholder: null, - resultConversion: null, - LookupResultKind.Empty, - CreateErrorType(), - hasErrors: true); - } + operandSyntax.CheckDeconstructionCompatibleArgument(diagnostics); - // The operand has to be a variable, property or indexer, so it must have a type. - var operandType = operand.Type; - Debug.Assert(operandType is not null); + BoundExpression operand = BindToNaturalType(BindValue(operandSyntax, diagnostics, BindValueKind.IncrementDecrement), diagnostics); + UnaryOperatorKind kind = SyntaxKindToUnaryOperatorKind(node.Kind()); - if (operandType.IsDynamic()) - { - return new BoundIncrementOperator( - node, - kind.WithType(UnaryOperatorKind.Dynamic).WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), - operand, - methodOpt: null, - constrainedToTypeOpt: null, - operandPlaceholder: null, - operandConversion: null, - resultPlaceholder: null, - resultConversion: null, - resultKind: LookupResultKind.Viable, - originalUserDefinedOperatorsOpt: default(ImmutableArray), - type: operandType, - hasErrors: false); - } + // If the operand is bad, avoid generating cascading errors. + if (operand.HasAnyErrors) + { + // NOTE: no candidate user-defined operators. + return new BoundIncrementOperator( + node, + kind, + operand, + methodOpt: null, + constrainedToTypeOpt: null, + operandPlaceholder: null, + operandConversion: null, + resultPlaceholder: null, + resultConversion: null, + LookupResultKind.Empty, + CreateErrorType(), + hasErrors: true); + } - bool isChecked = CheckOverflowAtRuntime; + // The operand has to be a variable, property or indexer, so it must have a type. + var operandType = operand.Type; + Debug.Assert(operandType is not null); - // Try an in-place user-defined operator - InstanceUserDefinedIncrementUsageMode mode = getInstanceUserDefinedIncrementUsageMode(node, kind, isChecked, operand, out string? checkedInstanceOperatorName, out string? ordinaryInstanceOperatorName); + if (operandType.IsDynamic()) + { + return new BoundIncrementOperator( + node, + kind.WithType(UnaryOperatorKind.Dynamic).WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), + operand, + methodOpt: null, + constrainedToTypeOpt: null, + operandPlaceholder: null, + operandConversion: null, + resultPlaceholder: null, + resultConversion: null, + resultKind: LookupResultKind.Viable, + originalUserDefinedOperatorsOpt: default(ImmutableArray), + type: operandType, + hasErrors: false); + } - if (mode != InstanceUserDefinedIncrementUsageMode.None) - { - Debug.Assert(ordinaryInstanceOperatorName is not null); + bool isChecked = CheckOverflowAtRuntime; - BoundIncrementOperator? inPlaceResult = tryApplyUserDefinedInstanceOperator(node, operatorToken, kind, mode, isChecked, checkedInstanceOperatorName, ordinaryInstanceOperatorName, operand, diagnostics); - if (inPlaceResult is not null) + // Try an in-place user-defined operator + InstanceUserDefinedIncrementUsageMode mode = getInstanceUserDefinedIncrementUsageMode(node, kind, isChecked, operand, out string? checkedInstanceOperatorName, out string? ordinaryInstanceOperatorName); + + if (mode != InstanceUserDefinedIncrementUsageMode.None) { - return inPlaceResult; + Debug.Assert(ordinaryInstanceOperatorName is not null); + + BoundIncrementOperator? inPlaceResult = tryApplyUserDefinedInstanceOperator(node, operatorToken, kind, mode, isChecked, checkedInstanceOperatorName, ordinaryInstanceOperatorName, operand, ref operatorResolutionForReporting, diagnostics); + if (inPlaceResult is not null) + { + return inPlaceResult; + } } - } - OverloadResolution.GetStaticUserDefinedUnaryOperatorMethodNames(kind, isChecked, out string staticOperatorName1, out string? staticOperatorName2Opt); + OverloadResolution.GetStaticUserDefinedUnaryOperatorMethodNames(kind, isChecked, out string staticOperatorName1, out string? staticOperatorName2Opt); - LookupResultKind resultKind; - ImmutableArray originalUserDefinedOperators; - var best = this.UnaryOperatorNonExtensionOverloadResolution(kind, isChecked, staticOperatorName1, staticOperatorName2Opt, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators); + LookupResultKind resultKind; + ImmutableArray originalUserDefinedOperators; + var best = this.UnaryOperatorNonExtensionOverloadResolution(kind, isChecked, staticOperatorName1, staticOperatorName2Opt, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); - Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); - Debug.Assert(best.HasValue == (resultKind is LookupResultKind.Viable)); - Debug.Assert(resultKind is not (LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty) || originalUserDefinedOperators.IsEmpty); + Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); + Debug.Assert(best.HasValue == (resultKind is LookupResultKind.Viable)); + Debug.Assert(resultKind is not (LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty) || originalUserDefinedOperators.IsEmpty); - if (!best.HasValue && resultKind != LookupResultKind.Ambiguous) - { - Debug.Assert(resultKind is LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); - Debug.Assert(originalUserDefinedOperators.IsEmpty); + if (!best.HasValue && resultKind != LookupResultKind.Ambiguous) + { + Debug.Assert(resultKind is LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); + Debug.Assert(originalUserDefinedOperators.IsEmpty); - // Check for extension operators - LookupResultKind staticExtensionResultKind; - ImmutableArray staticExtensionOriginalUserDefinedOperators; - UnaryOperatorAnalysisResult? staticExtensionBest; - BoundIncrementOperator? instanceExtensionResult = tryApplyUserDefinedExtensionOperator( - node, kind, mode, isChecked, - staticOperatorName1, staticOperatorName2Opt, - checkedInstanceOperatorName, ordinaryInstanceOperatorName, - operand, diagnostics, - out staticExtensionBest, out staticExtensionResultKind, out staticExtensionOriginalUserDefinedOperators); + // Check for extension operators + LookupResultKind staticExtensionResultKind; + ImmutableArray staticExtensionOriginalUserDefinedOperators; + UnaryOperatorAnalysisResult? staticExtensionBest; + BoundIncrementOperator? instanceExtensionResult = tryApplyUserDefinedExtensionOperator( + node, kind, mode, isChecked, + staticOperatorName1, staticOperatorName2Opt, + checkedInstanceOperatorName, ordinaryInstanceOperatorName, + operand, diagnostics, + out staticExtensionBest, out staticExtensionResultKind, out staticExtensionOriginalUserDefinedOperators, + ref operatorResolutionForReporting); - if (instanceExtensionResult is not null) - { - Debug.Assert(instanceExtensionResult.ResultKind is LookupResultKind.Viable || !instanceExtensionResult.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty); - return instanceExtensionResult; + if (instanceExtensionResult is not null) + { + Debug.Assert(instanceExtensionResult.ResultKind is LookupResultKind.Viable || !instanceExtensionResult.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty); + return instanceExtensionResult; + } + + if (staticExtensionBest.HasValue) + { + best = staticExtensionBest.GetValueOrDefault(); + resultKind = staticExtensionResultKind; + originalUserDefinedOperators = staticExtensionOriginalUserDefinedOperators; + } } - if (staticExtensionBest.HasValue) + if (!best.HasValue) { - best = staticExtensionBest.GetValueOrDefault(); - resultKind = staticExtensionResultKind; - originalUserDefinedOperators = staticExtensionOriginalUserDefinedOperators; + ReportUnaryOperatorError(node, diagnostics, operatorToken.Text, operand, resultKind, ref operatorResolutionForReporting); + return new BoundIncrementOperator( + node, + kind, + operand, + methodOpt: null, + constrainedToTypeOpt: null, + operandPlaceholder: null, + operandConversion: null, + resultPlaceholder: null, + resultConversion: null, + resultKind, + originalUserDefinedOperators, + CreateErrorType(), + hasErrors: true); } - } - - if (!best.HasValue) - { - ReportUnaryOperatorError(node, diagnostics, operatorToken.Text, operand, resultKind); - return new BoundIncrementOperator( - node, - kind, - operand, - methodOpt: null, - constrainedToTypeOpt: null, - operandPlaceholder: null, - operandConversion: null, - resultPlaceholder: null, - resultConversion: null, - resultKind, - originalUserDefinedOperators, - CreateErrorType(), - hasErrors: true); - } - var signature = best.Signature; + var signature = best.Signature; - CheckNativeIntegerFeatureAvailability(signature.Kind, node, diagnostics); - CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, signature.Method, isUnsignedRightShift: false, signature.ConstrainedToTypeOpt, diagnostics); + CheckNativeIntegerFeatureAvailability(signature.Kind, node, diagnostics); + CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, signature.Method, isUnsignedRightShift: false, signature.ConstrainedToTypeOpt, diagnostics); - var resultPlaceholder = new BoundValuePlaceholder(node, signature.ReturnType).MakeCompilerGenerated(); + var resultPlaceholder = new BoundValuePlaceholder(node, signature.ReturnType).MakeCompilerGenerated(); - BoundExpression? resultConversion = GenerateConversionForAssignment(operandType, resultPlaceholder, diagnostics, ConversionForAssignmentFlags.IncrementAssignment); + BoundExpression? resultConversion = GenerateConversionForAssignment(operandType, resultPlaceholder, diagnostics, ConversionForAssignmentFlags.IncrementAssignment); - bool hasErrors = resultConversion.HasErrors; + bool hasErrors = resultConversion.HasErrors; - if (resultConversion is not BoundConversion) - { - Debug.Assert(hasErrors || (object)resultConversion == resultPlaceholder); - if ((object)resultConversion != resultPlaceholder) + if (resultConversion is not BoundConversion) { - resultPlaceholder = null; - resultConversion = null; + Debug.Assert(hasErrors || (object)resultConversion == resultPlaceholder); + if ((object)resultConversion != resultPlaceholder) + { + resultPlaceholder = null; + resultConversion = null; + } } - } - if (!hasErrors && operandType.IsVoidPointer()) - { - Debug.Assert(!signature.Kind.IsUserDefined()); - Error(diagnostics, ErrorCode.ERR_VoidError, node); - hasErrors = true; - } + if (!hasErrors && operandType.IsVoidPointer()) + { + Debug.Assert(!signature.Kind.IsUserDefined()); + Error(diagnostics, ErrorCode.ERR_VoidError, node); + hasErrors = true; + } - var operandPlaceholder = new BoundValuePlaceholder(operand.Syntax, operandType).MakeCompilerGenerated(); - var operandConversion = CreateConversion(node, operandPlaceholder, best.Conversion, isCast: false, conversionGroupOpt: null, best.Signature.OperandType, diagnostics); + var operandPlaceholder = new BoundValuePlaceholder(operand.Syntax, operandType).MakeCompilerGenerated(); + var operandConversion = CreateConversion(node, operandPlaceholder, best.Conversion, isCast: false, conversionGroupOpt: null, best.Signature.OperandType, diagnostics); - return new BoundIncrementOperator( - node, - signature.Kind.WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), - operand, - signature.Method, - signature.ConstrainedToTypeOpt, - operandPlaceholder, - operandConversion, - resultPlaceholder, - resultConversion, - resultKind, - originalUserDefinedOperators, - operandType, - hasErrors); + return new BoundIncrementOperator( + node, + signature.Kind.WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), + operand, + signature.Method, + signature.ConstrainedToTypeOpt, + operandPlaceholder, + operandConversion, + resultPlaceholder, + resultConversion, + resultKind, + originalUserDefinedOperators, + operandType, + hasErrors); + } InstanceUserDefinedIncrementUsageMode getInstanceUserDefinedIncrementUsageMode( ExpressionSyntax node, @@ -3400,6 +3501,7 @@ InstanceUserDefinedIncrementUsageMode getInstanceUserDefinedIncrementUsageMode( string? checkedName, string ordinaryName, BoundExpression operand, + ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { Debug.Assert(operand.Type is not null); @@ -3423,7 +3525,7 @@ InstanceUserDefinedIncrementUsageMode getInstanceUserDefinedIncrementUsageMode( } AnalyzedArguments? analyzedArguments = null; - BoundIncrementOperator? inPlaceResult = tryInstanceOperatorOverloadResolutionAndFreeMethods(node, operatorToken, kind, mode, isChecked, isExtension: false, operand, ref analyzedArguments, methods, diagnostics); + BoundIncrementOperator? inPlaceResult = tryInstanceOperatorOverloadResolutionAndFreeMethods(node, operatorToken, kind, mode, isChecked, isExtension: false, operand, ref analyzedArguments, methods, ref operatorResolutionForReporting, diagnostics); Debug.Assert(analyzedArguments is not null); analyzedArguments.Free(); @@ -3440,6 +3542,7 @@ InstanceUserDefinedIncrementUsageMode getInstanceUserDefinedIncrementUsageMode( BoundExpression operand, ref AnalyzedArguments? analyzedArguments, ArrayBuilder methods, + ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { Debug.Assert(!methods.IsEmpty); @@ -3563,7 +3666,11 @@ InstanceUserDefinedIncrementUsageMode getInstanceUserDefinedIncrementUsageMode( methods.Free(); } - overloadResolutionResult.Free(); + if (!operatorResolutionForReporting.SaveResult(overloadResolutionResult, isExtension)) + { + overloadResolutionResult.Free(); + } + return inPlaceResult; } @@ -3588,7 +3695,8 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol operandType, Instance BindingDiagnosticBag diagnostics, out UnaryOperatorAnalysisResult? staticBest, out LookupResultKind staticResultKind, - out ImmutableArray staticOriginalUserDefinedOperators) + out ImmutableArray staticOriginalUserDefinedOperators, + ref OperatorResolutionForReporting operatorResolutionForReporting) { Debug.Assert(operand.Type is not null); @@ -3596,7 +3704,7 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol operandType, Instance staticResultKind = LookupResultKind.Empty; staticOriginalUserDefinedOperators = []; - var result = UnaryOperatorOverloadResolutionResult.GetInstance(); + UnaryOperatorOverloadResolutionResult? result = UnaryOperatorOverloadResolutionResult.GetInstance(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var extensionCandidatesInSingleScope = ArrayBuilder.GetInstance(); BoundIncrementOperator? inPlaceResult = null; @@ -3617,7 +3725,7 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol operandType, Instance inPlaceResult = tryApplyUserDefinedInstanceExtensionOperatorInSingleScope( node, operatorToken, extensionCandidatesInSingleScope, kind, mode, isChecked, checkedInstanceOperatorName, ordinaryInstanceOperatorName, - operand, ref analyzedArguments, diagnostics); + operand, ref analyzedArguments, ref operatorResolutionForReporting, diagnostics); if (inPlaceResult is not null) { break; @@ -3635,15 +3743,26 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol operandType, Instance operand, result, ref useSiteInfo)) { staticBest = AnalyzeUnaryOperatorOverloadResolutionResult(result, kind, operand, node, diagnostics, out staticResultKind, out staticOriginalUserDefinedOperators); + + if (operatorResolutionForReporting.SaveResult(result, isExtension: true)) + { + result = null; + } + break; } + + if (operatorResolutionForReporting.SaveResult(result, isExtension: true)) + { + result = UnaryOperatorOverloadResolutionResult.GetInstance(); + } } diagnostics.Add(node, useSiteInfo); analyzedArguments?.Free(); extensionCandidatesInSingleScope.Free(); - result.Free(); + result?.Free(); return inPlaceResult; } @@ -3658,6 +3777,7 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol operandType, Instance string ordinaryName, BoundExpression operand, ref AnalyzedArguments? analyzedArguments, + ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { Debug.Assert(mode != InstanceUserDefinedIncrementUsageMode.None); @@ -3674,7 +3794,7 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol operandType, Instance return null; } - return tryInstanceOperatorOverloadResolutionAndFreeMethods(node, operatorToken, kind, mode, isChecked, isExtension: true, operand, ref analyzedArguments, methods, diagnostics); + return tryInstanceOperatorOverloadResolutionAndFreeMethods(node, operatorToken, kind, mode, isChecked, isExtension: true, operand, ref analyzedArguments, methods, ref operatorResolutionForReporting, diagnostics); } } @@ -4268,10 +4388,14 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper LookupResultKind resultKind; ImmutableArray originalUserDefinedOperators; - var best = this.UnaryOperatorOverloadResolution(kind, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators); + OperatorResolutionForReporting operatorResolutionForReporting = default; + + var best = this.UnaryOperatorOverloadResolution(kind, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); if (!best.HasValue) { - ReportUnaryOperatorError(node, diagnostics, operatorText, operand, resultKind); + ReportUnaryOperatorError(node, diagnostics, operatorText, operand, resultKind, ref operatorResolutionForReporting); + operatorResolutionForReporting.Free(); + return new BoundUnaryOperator( node, kind, @@ -4285,6 +4409,7 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper hasErrors: true); } + operatorResolutionForReporting.Free(); var signature = best.Signature; var resultOperand = CreateConversion(operand.Syntax, operand, best.Conversion, isCast: false, conversionGroupOpt: null, signature.OperandType, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 4aec3aca8efc7..daf9fd64ddd7f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -2742,7 +2742,10 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, BindingDia // It was not. Does it implement operator true? expr = BindToNaturalType(expr, diagnostics); - var best = this.UnaryOperatorOverloadResolution(UnaryOperatorKind.True, expr, node, diagnostics, out LookupResultKind resultKind, out ImmutableArray originalUserDefinedOperators); + OperatorResolutionForReporting discardedOperatorResolutionForReporting = default; + var best = this.UnaryOperatorOverloadResolution(UnaryOperatorKind.True, expr, node, diagnostics, out LookupResultKind resultKind, out ImmutableArray originalUserDefinedOperators, ref discardedOperatorResolutionForReporting); + discardedOperatorResolutionForReporting.Free(); + if (!best.HasValue) { // No. Give a "not convertible to bool" error. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs index e064e9e9c5d9b..7bc941119f54a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs @@ -158,7 +158,10 @@ private void PrepareBoolConversionAndTruthOperator(TypeSymbol type, BinaryExpres LookupResultKind resultKind; ImmutableArray originalUserDefinedOperators; BoundExpression comparisonResult = new BoundTupleOperandPlaceholder(node, type); - UnaryOperatorAnalysisResult best = this.UnaryOperatorOverloadResolution(boolOpKind, comparisonResult, node, diagnostics, out resultKind, out originalUserDefinedOperators); + OperatorResolutionForReporting discardedOperatorResolutionForReporting = default; + UnaryOperatorAnalysisResult best = this.UnaryOperatorOverloadResolution(boolOpKind, comparisonResult, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref discardedOperatorResolutionForReporting); + discardedOperatorResolutionForReporting.Free(); + if (best.HasValue) { conversionForBoolPlaceholder = new BoundValuePlaceholder(node, type).MakeCompilerGenerated(); @@ -208,7 +211,10 @@ private TupleBinaryOperatorInfo.Multiple BindTupleBinaryOperatorNestedInfo(Binar if (left.IsLiteralDefaultOrImplicitObjectCreation() || right.IsLiteralDefaultOrImplicitObjectCreation()) { - ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, LookupResultKind.Ambiguous); + OperatorResolutionForReporting discardedOperatorResolutionForReporting = default; + ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, LookupResultKind.Ambiguous, ref discardedOperatorResolutionForReporting); + discardedOperatorResolutionForReporting.Free(); + return TupleBinaryOperatorInfo.Multiple.ErrorInstance; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs index 35d044d2b9714..230af1cdcaec3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs @@ -4,13 +4,14 @@ #nullable disable +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { [SuppressMessage("Performance", "CA1067", Justification = "Equality not actually implemented")] + [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] internal readonly struct BinaryOperatorAnalysisResult : IMemberResolutionResultWithPriority { public readonly Conversion LeftConversion; @@ -65,5 +66,10 @@ public BinaryOperatorAnalysisResult Worse() { return new BinaryOperatorAnalysisResult(OperatorAnalysisResultKind.Worse, this.Signature, this.LeftConversion, this.RightConversion); } + + private string GetDebuggerDisplay() + { + return $"{Signature.Kind} {Kind} {Signature.Method?.ToDisplayString()}"; + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorAnalysisResultKind.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorAnalysisResultKind.cs index 8e0e99e89f20d..4b0f67a74875d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorAnalysisResultKind.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorAnalysisResultKind.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; - namespace Microsoft.CodeAnalysis.CSharp { internal enum OperatorAnalysisResultKind : byte diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs index c5ac83e989313..4009b68133cd0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs @@ -4,10 +4,12 @@ #nullable disable +using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; namespace Microsoft.CodeAnalysis.CSharp { + [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] internal readonly struct UnaryOperatorAnalysisResult : IMemberResolutionResultWithPriority { public readonly UnaryOperatorSignature Signature; @@ -48,5 +50,10 @@ public UnaryOperatorAnalysisResult Worse() { return new UnaryOperatorAnalysisResult(OperatorAnalysisResultKind.Worse, this.Signature, this.Conversion); } + + private string GetDebuggerDisplay() + { + return $"{Signature.Kind} {Kind} {Signature.Method?.ToDisplayString()}"; + } } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index f3c3e12fea250..581a1e9f37a35 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -8171,4 +8171,13 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index eaa2270e35dc6..ad93cbed18c23 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2436,6 +2436,9 @@ internal enum ErrorCode ERR_BadVisBaseType = 9338, ERR_AmbigExtension = 9339, + ERR_SingleInapplicableBinaryOperator = 9340, + ERR_SingleInapplicableUnaryOperator = 9341, + ERR_AmbigOperator = 9342, // Note: you will need to do the following after adding errors: // 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 3f65415dcadcb..e1b55ba3daa84 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2544,6 +2544,9 @@ or ErrorCode.WRN_RedundantPattern or ErrorCode.HDN_RedundantPatternStackGuard or ErrorCode.ERR_BadVisBaseType or ErrorCode.ERR_AmbigExtension + or ErrorCode.ERR_SingleInapplicableBinaryOperator + or ErrorCode.ERR_SingleInapplicableUnaryOperator + or ErrorCode.ERR_AmbigOperator => false, }; #pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 6692aff329923..046f20e466eea 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. Identifikátor {0} je v tomto kontextu nejednoznačný mezi typem {1} a parametrem {2}. @@ -2387,6 +2392,16 @@ Program, který používá příkazy nejvyšší úrovně, musí být spustitelný. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' Spread operátor .. nejde použít pro proměnné typu {0}, protože {0} neobsahuje veřejnou definici instance nebo rozšíření pro {1}. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 97843e4508ac6..36dd240b4a333 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. Bezeichner „{0}“ ist zwischen Typ „{1}“ und Parameter „{2}“ in diesem Kontext mehrdeutig. @@ -2387,6 +2392,16 @@ Das Programm mit Anweisungen der obersten Ebene muss eine ausführbare Datei sein. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' Der Überfüllungsoperator ".." kann nicht für Variablen vom Typ "{0}" verwendet werden, weil "{0}" keine öffentliche Instanz- oder Erweiterungsdefinition für "{1}" enthält. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 640e38d85a40a..635df60d0f4c0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. El identificador '{0}' es ambiguo entre el tipo '{1}' y el parámetro '{2}' en este contexto. @@ -2387,6 +2392,16 @@ El programa que usa instrucciones de nivel superior debe ser un ejecutable. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' El operador de propagación ".." no puede funcionar en variables de tipo "{0}" porque "{0}" no contiene una instancia pública o una definición de extensión para "{1}" diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 377bd6f3b7102..2b51b8f1cbe64 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. L’identificateur '{0}' est ambiguë entre le type '{1}' et le paramètre '{2}' dans ce contexte. @@ -2387,6 +2392,16 @@ Le programme qui utilise des instructions de niveau supérieur doit être un exécutable. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' L’opérateur spread « .. » ne peut pas fonctionner sur des variables de type « {0} », car « {0} » ne contient pas de définition d’extension ou d’instance publique pour « {1} » diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index e3898030f293d..22fe251935e54 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. L'identificatore '{0}' è ambiguo tra il tipo '{1}' e il parametro '{2}'. @@ -2387,6 +2392,16 @@ Il programma che usa istruzioni di primo livello deve essere un eseguibile. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' L'operatore spread '..' non può operare su variabili di tipo '{0}' perché '{0}' non contiene un'istanza pubblica o una definizione di estensione per '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index e1195232095fc..1aa5ada1c3e6c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. 識別子 '{0}' は、このコンテキストの型 '{1}' とパラメーター '{2}' の間であいまいです。 @@ -2387,6 +2392,16 @@ トップレベルのステートメントを使用するプログラムは、実行可能ファイルである必要があります。 + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' '{0}' が '{1}' のパブリック インスタンス定義または拡張機能定義を含んでいないため、スプレッド演算子 '..' は型 '{0}' の変数に対して使用できません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index df5e455c7c1cd..ffc2cf6ff4218 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. '{0}' 식별자는 이 컨텍스트에서 '{1}' 형식과 '{2}' 매개 변수 사이에 모호합니다. @@ -2387,6 +2392,16 @@ 최상위 문을 사용하는 프로그램은 실행 파일이어야 합니다. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' '..' 스프레드 연산자는 '{0}' 형식의 변수에서 '{0}'에 '{1}'에 대한 공용 인스턴스 또는 확장 정의가 없으므로 작동할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 265ab008aca49..30957cb6a701b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. Identyfikator '{0}' jest niejednoznaczny między typem '{1}' i parametrem '{2}' w tym kontekście. @@ -2387,6 +2392,16 @@ Program korzystający z instrukcji najwyższego poziomu musi być plikiem wykonywalnym. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' Operator rozłożenia „..” nie może operować na zmiennych typu „{0}”, ponieważ „{0}” nie zawiera publicznego wystąpienia lub definicji rozszerzenia dla elementu „{1}” diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index e4073b83d989f..4c00dd6e968d4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. O identificador "{0}" é ambíguo entre o tipo "{1}" e o parâmetro "{2}" neste contexto. @@ -2387,6 +2392,16 @@ O programa que usa as instruções de nível superior precisa ser um executável. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' O operador de propagação '..' não pode operar em variáveis do tipo '{0}' porque '{0}' não contém uma instância pública ou definição de extensão para '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index c5bab51e059c7..eccbc3d6599b7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. Идентификатор "{0}" является неоднозначным между типом "{1}" и параметром "{2}" в этом контексте. @@ -2387,6 +2392,16 @@ Программы, использующие инструкции верхнего уровня, должны быть исполняемыми. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' Оператор расширения ".." не работает с переменными типа "{0}", так как "{0}" не содержит открытое определение экземпляра или расширения для "{1}" diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a5813b5e983ad..712d7fc9ed644 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. Bu bağlamda '{0}' tanımlayıcısı, '{1}' tipi ve '{2}' parametresi arasında belirsizdir. @@ -2387,6 +2392,16 @@ Üst düzey deyimleri kullanan program yürütülebilir olmalıdır. + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' '{0}', '{0}' için bir genel örnek veya uzantı tanımı içermediğinden '..' yayılma işleci, '{1}' türündeki değişkenler üzerinde çalışamaz diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index ef653ed8d691b..cbc98dff802d5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. 在此上下文中,标识符“{0}”在类型“{1}”和参数“{2}”之间不明确。 @@ -2387,6 +2392,16 @@ 使用顶级语句的程序必须是可执行文件。 + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' 由于“{0}”不包含“{1}”的公共实例或扩展定义,因此展开运算符 “...” 不能作用于“{0}”类型的变量 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index ffebbc3e53841..754f46b218488 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -72,6 +72,11 @@ The extension resolution is ambiguous between the following members: '{0}' and '{1}' + + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + Operator resolution is ambiguous between the following members: '{0}' and '{1}' + + Identifier '{0}' is ambiguous between type '{1}' and parameter '{2}' in this context. 在此內容中,類型 '{1}' 與參數 '{2}' 之間的識別碼 '{0}' 不明確。 @@ -2387,6 +2392,16 @@ 使用最上層陳述式的程式必須是可執行檔。 + + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + + + + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + + Spread operator '..' cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}' 擴展運算子 '..' 無法在類型 '{0}' 的變數上運作,因為 '{0}' 不包含 '{1}' 的公用執行個體或延伸模組定義 diff --git a/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs index a6b6516bf8d82..d6828a92ca2fa 100644 --- a/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs @@ -2006,9 +2006,9 @@ public class C2 "; var expectedDiagnostics = new[] { - // (4,17): error CS0034: Operator '+' is ambiguous on operands of type 'C1' and 'C2' + // (4,20): error CS9342: Operator resolution is ambiguous between the following members: 'C1.operator +(C1, C2)' and 'C2.operator +(C1, C2)' // _ = c1 + c2; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "c1 + c2").WithArguments("+", "C1", "C2").WithLocation(4, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("C1.operator +(C1, C2)", "C2.operator +(C1, C2)").WithLocation(4, 20) }; CreateCompilation([executable, source, OverloadResolutionPriorityAttributeDefinition]).VerifyDiagnostics(expectedDiagnostics); diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs index 1f5f9540b6265..34b469e6e31e9 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs @@ -958,9 +958,9 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); comp.VerifyEmitDiagnostics( - // (32,17): error CS0035: Operator '-' is ambiguous on an operand of type 'I2' + // (32,17): error CS9339: Operator resolution is ambiguous between the following members:'I1.operator -(I1)' and 'I3.operator -(I3)' // var y = -x; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "-x").WithArguments("-", "I2").WithLocation(32, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("I1.operator -(I1)", "I3.operator -(I3)").WithLocation(32, 17) ); var tree = comp.SyntaxTrees.First(); @@ -1024,11 +1024,10 @@ static void Main() var comp = CreateCompilation(src); - // https://github.com/dotnet/roslyn/issues/78830: We might want to include more information into the error. Like what methods conflict. comp.VerifyEmitDiagnostics( - // (34,21): error CS0035: Operator '-' is ambiguous on an operand of type 'I2' + // (34,21): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(I1).operator -(I1)' and 'Extensions2.extension(I3).operator -(I3)' // var y = -x; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "-x").WithArguments("-", "I2").WithLocation(34, 21) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("NS1.Extensions2.extension(I1).operator -(I1)", "NS1.Extensions2.extension(I3).operator -(I3)").WithLocation(34, 21) ); var tree = comp.SyntaxTrees.First(); @@ -1732,14 +1731,25 @@ static void Main() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); +#if DEBUG comp.VerifyEmitDiagnostics( - // (35,13): error CS0035: Operator '-' is ambiguous on an operand of type 'C1' + // (35,13): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(C1).operator -(C1)' and 'Extensions1.extension(C1).operator -(C1)' // _ = -c1; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "-c1").WithArguments("-", "C1").WithLocation(35, 13), - // (39,17): error CS0035: Operator '-' is ambiguous on an operand of type 'C1' + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions2.extension(C1).operator -(C1)", "Extensions1.extension(C1).operator -(C1)").WithLocation(35, 13), + // (39,17): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator checked -(C1)' and 'Extensions2.extension(C1).operator -(C1)' // _ = -c1; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "-c1").WithArguments("-", "C1").WithLocation(39, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions1.extension(C1).operator checked -(C1)", "Extensions2.extension(C1).operator -(C1)").WithLocation(39, 17) ); +#else + comp.VerifyEmitDiagnostics( + // (35,13): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator -(C1)' and 'Extensions2.extension(C1).operator -(C1)' + // _ = -c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions1.extension(C1).operator -(C1)", "Extensions2.extension(C1).operator -(C1)").WithLocation(35, 13), + // (39,17): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator checked -(C1)' and 'Extensions2.extension(C1).operator -(C1)' + // _ = -c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions1.extension(C1).operator checked -(C1)", "Extensions2.extension(C1).operator -(C1)").WithLocation(39, 17) + ); +#endif var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); @@ -4258,11 +4268,19 @@ static void Main() """ + CompilerFeatureRequiredAttribute; var comp = CreateCompilation(src, options: TestOptions.DebugExe); +#if DEBUG + comp.VerifyDiagnostics( + // (26,13): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(S2).operator -(S2)' and 'Extensions1.extension(S2).operator -(S2)' + // _ = -s2; + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions2.extension(S2).operator -(S2)", "Extensions1.extension(S2).operator -(S2)").WithLocation(26, 13) + ); +#else comp.VerifyDiagnostics( - // (26,13): error CS0035: Operator '-' is ambiguous on an operand of type 'S2' + // (26,13): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(S2).operator -(S2)' and 'Extensions2.extension(S2).operator -(S2)' // _ = -s2; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "-s2").WithArguments("-", "S2").WithLocation(26, 13) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions1.extension(S2).operator -(S2)", "Extensions2.extension(S2).operator -(S2)").WithLocation(26, 13) ); +#endif var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); @@ -5839,9 +5857,9 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); comp.VerifyEmitDiagnostics( - // (33,17): error CS0035: Operator '--' is ambiguous on an operand of type 'I2' + // (33,17): error CS9339: Operator resolution is ambiguous between the following members:'I1.operator --(I1)' and 'I3.operator --(I3)' // var y = --x; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "--x").WithArguments("--", "I2").WithLocation(33, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "--").WithArguments("I1.operator --(I1)", "I3.operator --(I3)").WithLocation(33, 17) ); var tree = comp.SyntaxTrees.First(); @@ -6035,11 +6053,10 @@ static void Main() var comp = CreateCompilation(src); - // https://github.com/dotnet/roslyn/issues/78830: We might want to include more information into the error. Like what methods conflict. comp.VerifyEmitDiagnostics( - // (35,21): error CS0035: Operator '--' is ambiguous on an operand of type 'I2' + // (35,21): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(I1).operator --(I1)' and 'Extensions2.extension(I3).operator --(I3)' // var y = --x; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "--x").WithArguments("--", "I2").WithLocation(35, 21) + Diagnostic(ErrorCode.ERR_AmbigOperator, "--").WithArguments("NS1.Extensions2.extension(I1).operator --(I1)", "NS1.Extensions2.extension(I3).operator --(I3)").WithLocation(35, 21) ); var tree = comp.SyntaxTrees.First(); @@ -6341,9 +6358,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (20,13): error CS0023: Operator '++' cannot be applied to operand of type 'S1?' + // (20,13): error CS9341: Operator cannot be applied on operand of type 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator ++()' // _ = ++s1; - Diagnostic(ErrorCode.ERR_BadUnaryOp, "++s1").WithArguments("++", "S1?").WithLocation(20, 13) + Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1?", "Extensions1.extension(ref S1).operator ++()").WithLocation(20, 13) ); } @@ -6463,9 +6480,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (19,13): error CS0023: Operator '++' cannot be applied to operand of type 'S1' + // (19,13): error CS9341: Operator cannot be applied on operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1?).operator ++()' // _ = ++s1; - Diagnostic(ErrorCode.ERR_BadUnaryOp, "++s1").WithArguments("++", "S1").WithLocation(19, 13), + Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1", "Extensions1.extension(ref S1?).operator ++()").WithLocation(19, 13), // (20,44): error CS1620: Argument 1 must be passed with the 'ref' keyword // Extensions1.op_IncrementAssignment(s1); Diagnostic(ErrorCode.ERR_BadArgRef, "s1").WithArguments("1", "ref").WithLocation(20, 44), @@ -6562,15 +6579,15 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (22,13): error CS0023: Operator '++' cannot be applied to operand of type 'S1' + // (22,13): error CS9341: Operator cannot be applied on operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator ++()' // _ = ++s1; - Diagnostic(ErrorCode.ERR_BadUnaryOp, "++s1").WithArguments("++", "S1").WithLocation(22, 13), + Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1", "Extensions1.extension(ref S2).operator ++()").WithLocation(22, 13), // (23,48): error CS1503: Argument 1: cannot convert from 'ref S1' to 'ref S2' // Extensions1.op_IncrementAssignment(ref s1); Diagnostic(ErrorCode.ERR_BadArgType, "s1").WithArguments("1", "ref S1", "ref S2").WithLocation(23, 48), - // (26,13): error CS0023: Operator '++' cannot be applied to operand of type 'S1?' + // (26,13): error CS9341: Operator cannot be applied on operand of type 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator ++()' // _ = ++s2; - Diagnostic(ErrorCode.ERR_BadUnaryOp, "++s2").WithArguments("++", "S1?").WithLocation(26, 13), + Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1?", "Extensions1.extension(ref S2).operator ++()").WithLocation(26, 13), // (27,48): error CS1503: Argument 1: cannot convert from 'ref S1?' to 'ref S2' // Extensions1.op_IncrementAssignment(ref s2); Diagnostic(ErrorCode.ERR_BadArgType, "s2").WithArguments("1", "ref S1?", "ref S2").WithLocation(27, 48) @@ -6643,12 +6660,12 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,13): error CS0023: Operator '++' cannot be applied to operand of type 'S1' + // (17,13): error CS9341: Operator cannot be applied on operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(object).operator ++()' // _ = ++s1; - Diagnostic(ErrorCode.ERR_BadUnaryOp, "++s1").WithArguments("++", "S1").WithLocation(17, 13), - // (21,13): error CS0023: Operator '++' cannot be applied to operand of type 'S1?' + Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1", "Extensions1.extension(object).operator ++()").WithLocation(17, 13), + // (21,13): error CS9341: Operator cannot be applied on operand of type 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(object).operator ++()' // _ = ++s2; - Diagnostic(ErrorCode.ERR_BadUnaryOp, "++s2").WithArguments("++", "S1?").WithLocation(21, 13) + Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1?", "Extensions1.extension(object).operator ++()").WithLocation(21, 13) ); } @@ -6849,9 +6866,9 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,13): error CS0023: Operator '++' cannot be applied to operand of type 'int[]' + // (17,13): error CS9341: Operator cannot be applied on operand of type 'int[]'. The closest inapplicable candidate is 'Extensions1.extension(ref Span).operator ++()' // _ = ++a1; - Diagnostic(ErrorCode.ERR_BadUnaryOp, "++a1").WithArguments("++", "int[]").WithLocation(17, 13), + Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("int[]", "Extensions1.extension(ref System.Span).operator ++()").WithLocation(17, 13), // (18,48): error CS1503: Argument 1: cannot convert from 'ref int[]' to 'ref System.Span' // Extensions1.op_IncrementAssignment(ref a1); Diagnostic(ErrorCode.ERR_BadArgType, "a1").WithArguments("1", "ref int[]", "ref System.Span").WithLocation(18, 48) @@ -7190,9 +7207,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,13): error CS0023: Operator '++' cannot be applied to operand of type 'S1' + // (17,13): error CS9341: Operator cannot be applied on operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator ++()' // _ = ++s1; - Diagnostic(ErrorCode.ERR_BadUnaryOp, "++s1").WithArguments("++", "S1").WithLocation(17, 13) + Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1", "Extensions1.extension(ref S1).operator ++()").WithLocation(17, 13) ); } @@ -7647,14 +7664,25 @@ static void Main() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); +#if DEBUG + comp.VerifyEmitDiagnostics( + // (35,13): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(C1).operator --(C1)' and 'Extensions1.extension(C1).operator --(C1)' + // _ = --c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "--").WithArguments("Extensions2.extension(C1).operator --(C1)", "Extensions1.extension(C1).operator --(C1)").WithLocation(35, 13), + // (39,17): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator checked --(C1)' and 'Extensions2.extension(C1).operator --(C1)' + // _ = --c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "--").WithArguments("Extensions1.extension(C1).operator checked --(C1)", "Extensions2.extension(C1).operator --(C1)").WithLocation(39, 17) + ); +#else comp.VerifyEmitDiagnostics( - // (35,13): error CS0035: Operator '--' is ambiguous on an operand of type 'C1' + // (35,13): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator --(C1)' and 'Extensions2.extension(C1).operator --(C1)' // _ = --c1; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "--c1").WithArguments("--", "C1").WithLocation(35, 13), - // (39,17): error CS0035: Operator '--' is ambiguous on an operand of type 'C1' + Diagnostic(ErrorCode.ERR_AmbigOperator, "--").WithArguments("Extensions1.extension(C1).operator --(C1)", "Extensions2.extension(C1).operator --(C1)").WithLocation(35, 13), + // (39,17): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator checked --(C1)' and 'Extensions2.extension(C1).operator --(C1)' // _ = --c1; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "--c1").WithArguments("--", "C1").WithLocation(39, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "--").WithArguments("Extensions1.extension(C1).operator checked --(C1)", "Extensions2.extension(C1).operator --(C1)").WithLocation(39, 17) ); +#endif var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); @@ -10532,11 +10560,19 @@ static void Main() """ + CompilerFeatureRequiredAttribute; var comp = CreateCompilation(src, options: TestOptions.DebugExe); +#if DEBUG comp.VerifyDiagnostics( - // (26,9): error CS0035: Operator '++' is ambiguous on an operand of type 'S2' + // (26,9): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(S2).operator ++(S2)' and 'Extensions1.extension(S2).operator ++(S2)' // ++s2; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "++s2").WithArguments("++", "S2").WithLocation(26, 9) + Diagnostic(ErrorCode.ERR_AmbigOperator, "++").WithArguments("Extensions2.extension(S2).operator ++(S2)", "Extensions1.extension(S2).operator ++(S2)").WithLocation(26, 9) ); +#else + comp.VerifyDiagnostics( + // (26,9): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(S2).operator ++(S2)' and 'Extensions2.extension(S2).operator ++(S2)' + // ++s2; + Diagnostic(ErrorCode.ERR_AmbigOperator, "++").WithArguments("Extensions1.extension(S2).operator ++(S2)", "Extensions2.extension(S2).operator ++(S2)").WithLocation(26, 9) + ); +#endif var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); @@ -11828,9 +11864,9 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); comp.VerifyEmitDiagnostics( - // (32,17): error CS0034: Operator '-' is ambiguous on operands of type 'I2' and 'I2' + // (32,19): error CS9339: Operator resolution is ambiguous between the following members:'I1.operator -(I1, I1)' and 'I3.operator -(I3, I3)' // var y = x - x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x - x").WithArguments("-", "I2", "I2").WithLocation(32, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("I1.operator -(I1, I1)", "I3.operator -(I3, I3)").WithLocation(32, 19) ); var tree = comp.SyntaxTrees.First(); @@ -11894,11 +11930,10 @@ static void Main() var comp = CreateCompilation(src); - // https://github.com/dotnet/roslyn/issues/78830: We might want to include more information into the error. Like what methods conflict. comp.VerifyEmitDiagnostics( - // (34,21): error CS0034: Operator '-' is ambiguous on operands of type 'I2' and 'I2' + // (34,23): error CS9339: Operator resolution is ambiguous between the following members:'Extensions2.extension(I1).operator -(I1, I1)' and 'Extensions2.extension(I3).operator -(I3, I3)' // var y = x - x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x - x").WithArguments("-", "I2", "I2").WithLocation(34, 21) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("NS1.Extensions2.extension(I1).operator -(I1, I1)", "NS1.Extensions2.extension(I3).operator -(I3, I3)").WithLocation(34, 23) ); var tree = comp.SyntaxTrees.First(); @@ -13249,15 +13284,25 @@ static void Main() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); +#if DEBUG comp.VerifyEmitDiagnostics( - // (35,13): error CS0034: Operator '-' is ambiguous on operands of type 'C1' and 'C1' + // (35,16): error CS9339: Operator resolution is ambiguous between the following members:'Extensions2.extension(C1).operator -(C1, C1)' and 'Extensions1.extension(C1).operator -(C1, C1)' // _ = c1 - c1; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "c1 - c1").WithArguments("-", "C1", "C1").WithLocation(35, 13), - // (39,17): error CS0034: Operator '-' is ambiguous on operands of type 'C1' and 'C1' + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions2.extension(C1).operator -(C1, C1)", "Extensions1.extension(C1).operator -(C1, C1)").WithLocation(35, 16), + // (39,20): error CS9339: Operator resolution is ambiguous between the following members:'Extensions1.extension(C1).operator checked -(C1, C1)' and 'Extensions2.extension(C1).operator -(C1, C1)' // _ = c1 - c1; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "c1 - c1").WithArguments("-", "C1", "C1").WithLocation(39, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions1.extension(C1).operator checked -(C1, C1)", "Extensions2.extension(C1).operator -(C1, C1)").WithLocation(39, 20) ); - +#else + comp.VerifyEmitDiagnostics( + // (35,16): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator -(C1, C1)' and 'Extensions2.extension(C1).operator -(C1, C1)' + // _ = c1 - c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions1.extension(C1).operator -(C1, C1)", "Extensions2.extension(C1).operator -(C1, C1)").WithLocation(35, 16), + // (39,20): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator checked -(C1, C1)' and 'Extensions2.extension(C1).operator -(C1, C1)' + // _ = c1 - c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions1.extension(C1).operator checked -(C1, C1)", "Extensions2.extension(C1).operator -(C1, C1)").WithLocation(39, 20) + ); +#endif var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var opNode = tree.GetRoot().DescendantNodes().OfType().Last(); @@ -15167,9 +15212,9 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); comp.VerifyEmitDiagnostics( - // (38,17): error CS0034: Operator '&&' is ambiguous on operands of type 'I2' and 'I2' + // (38,19): error CS9339: Operator resolution is ambiguous between the following members:'I1.operator &(I1, I1)' and 'I3.operator &(I3, I3)' // var y = x && x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x && x").WithArguments("&&", "I2", "I2").WithLocation(38, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "&&").WithArguments("I1.operator &(I1, I1)", "I3.operator &(I3, I3)").WithLocation(38, 19) ); var tree = comp.SyntaxTrees.First(); @@ -15232,9 +15277,9 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); comp.VerifyEmitDiagnostics( - // (34,17): error CS0034: Operator '&&' is ambiguous on operands of type 'I2' and 'I2' + // (34,19): error CS9339: Operator resolution is ambiguous between the following members:'I1.operator &(I1, I1)' and 'I3.operator &(I3, I3)' // var y = x && x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x && x").WithArguments("&&", "I2", "I2").WithLocation(34, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "&&").WithArguments("I1.operator &(I1, I1)", "I3.operator &(I3, I3)").WithLocation(34, 19) ); } @@ -15290,11 +15335,10 @@ static void Main() var comp = CreateCompilation(src); - // https://github.com/dotnet/roslyn/issues/78830: We might want to include more information into the error. Like what methods conflict. comp.VerifyEmitDiagnostics( - // (40,21): error CS0034: Operator '&&' is ambiguous on operands of type 'I2' and 'I2' + // (40,23): error CS9339: Operator resolution is ambiguous between the following members:'Extensions2.extension(I1).operator &(I1, I1)' and 'Extensions2.extension(I3).operator &(I3, I3)' // var y = x && x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x && x").WithArguments("&&", "I2", "I2").WithLocation(40, 21) + Diagnostic(ErrorCode.ERR_AmbigOperator, "&&").WithArguments("NS1.Extensions2.extension(I1).operator &(I1, I1)", "NS1.Extensions2.extension(I3).operator &(I3, I3)").WithLocation(40, 23) ); var tree = comp.SyntaxTrees.First(); @@ -15360,9 +15404,9 @@ static void Main() var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (36,21): error CS0034: Operator '&&' is ambiguous on operands of type 'I2' and 'I2' + // (36,23): error CS9339: Operator resolution is ambiguous between the following members:'Extensions2.extension(I1).operator &(I1, I1)' and 'Extensions2.extension(I3).operator &(I3, I3)' // var y = x && x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x && x").WithArguments("&&", "I2", "I2").WithLocation(36, 21) + Diagnostic(ErrorCode.ERR_AmbigOperator, "&&").WithArguments("NS1.Extensions2.extension(I1).operator &(I1, I1)", "NS1.Extensions2.extension(I3).operator &(I3, I3)").WithLocation(36, 23) ); } @@ -15403,9 +15447,9 @@ static void Main() var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (26,17): error CS0034: Operator '&&' is ambiguous on operands of type 'I2' and 'I2' + // (26,19): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(I1).operator &(I1, I1)' and 'Extensions2.extension(I3).operator &(I3, I3)' // var y = x && x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x && x").WithArguments("&&", "I2", "I2").WithLocation(26, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "&&").WithArguments("Extensions2.extension(I1).operator &(I1, I1)", "Extensions2.extension(I3).operator &(I3, I3)").WithLocation(26, 19) ); } @@ -19867,9 +19911,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (22,13): error CS0019: Operator '+' cannot be applied to operands of type 'S2' and 'S2' + // (22,16): error CS9340: Operator cannot be applied on operands of type 'S2' and 'S2'. The closest inapplicable candidate is 'Extensions1.extension(S2).operator +(S2, int)' // _ = s2 + s2; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s2 + s2").WithArguments("+", "S2", "S2").WithLocation(22, 13) + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+").WithArguments("S2", "S2", "Extensions1.extension(S2).operator +(S2, int)").WithLocation(22, 16) ); var tree = comp.SyntaxTrees.First(); @@ -19922,9 +19966,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (29,13): error CS0034: Operator '+' is ambiguous on operands of type 'S2' and 'S2' + // (29,16): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions1.extension(S2).operator +(S2, C1)' and 'Extensions1.extension(S2).operator +(S2, C2)' // _ = s2 + s2; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "s2 + s2").WithArguments("+", "S2", "S2").WithLocation(29, 13) + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("Extensions1.extension(S2).operator +(S2, C1)", "Extensions1.extension(S2).operator +(S2, C2)").WithLocation(29, 16) ); var tree = comp.SyntaxTrees.First(); @@ -21475,9 +21519,9 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); comp.VerifyEmitDiagnostics( - // (33,17): error CS0034: Operator '-=' is ambiguous on operands of type 'I2' and 'I2' + // (33,19): error CS9339: Operator resolution is ambiguous between the following members:'I1.operator -(I1, I1)' and 'I3.operator -(I3, I3)' // var y = x -= x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x -= x").WithArguments("-=", "I2", "I2").WithLocation(33, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-=").WithArguments("I1.operator -(I1, I1)", "I3.operator -(I3, I3)").WithLocation(33, 19) ); var tree = comp.SyntaxTrees.First(); @@ -21671,11 +21715,10 @@ static void Main() var comp = CreateCompilation(src); - // https://github.com/dotnet/roslyn/issues/78830: We might want to include more information into the error. Like what methods conflict. comp.VerifyEmitDiagnostics( - // (35,21): error CS0034: Operator '-=' is ambiguous on operands of type 'I2' and 'I2' + // (35,23): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(I1).operator -(I1, I1)' and 'Extensions2.extension(I3).operator -(I3, I3)' // var y = x -= x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x -= x").WithArguments("-=", "I2", "I2").WithLocation(35, 21) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-=").WithArguments("NS1.Extensions2.extension(I1).operator -(I1, I1)", "NS1.Extensions2.extension(I3).operator -(I3, I3)").WithLocation(35, 23) ); var tree = comp.SyntaxTrees.First(); @@ -21926,11 +21969,11 @@ static void Main() """ + CompilerFeatureRequiredAttribute; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var comp = CreateCompilation(src); comp.VerifyDiagnostics( - // (18,13): error CS0019: Operator '+=' cannot be applied to operands of type 'S1?' and 'S1?' - // _ = s11 += s12; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s11 " + op + "= s12").WithArguments(op + "=", "S1?", "S1?").WithLocation(18, 13) + // (18,17): error CS9340: Operator cannot be applied on operands of type 'S1?' and 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator %=(S1)' + // _ = s11 %= s12; + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, op + "=").WithArguments("S1?", "S1?", "Extensions1.extension(ref S1).operator " + op + "=(S1)").WithLocation(18, 17) ); } @@ -22046,14 +22089,14 @@ static void Main() """ + CompilerFeatureRequiredAttribute; - var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var comp = CreateCompilation(src); comp.VerifyDiagnostics( - // (18,13): error CS0019: Operator '+=' cannot be applied to operands of type 'S1?' and 'S1' - // _ = s11 += s12; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s11 " + op + "= s12").WithArguments(op + "=", "S1?", "S1").WithLocation(18, 13), - // (19,13): error CS0019: Operator '+=' cannot be applied to operands of type 'S1' and 'S1?' - // _ = s12 += s11; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s12 " + op + "= s11").WithArguments(op + "=", "S1", "S1?").WithLocation(19, 13) + // (18,17): error CS9340: Operator cannot be applied on operands of type 'S1?' and 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator %=(S1)' + // _ = s11 %= s12; + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, op + "=").WithArguments("S1?", "S1", "Extensions1.extension(ref S1).operator " + op + "=(S1)").WithLocation(18, 17), + // (19,17): error CS9340: Operator cannot be applied on operands of type 'S1' and 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator %=(S1)' + // _ = s12 %= s11; + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, op + "=").WithArguments("S1", "S1?", "Extensions1.extension(ref S1).operator " + op + "=(S1)").WithLocation(19, 17) ); } @@ -22317,9 +22360,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,13): error CS0019: Operator '+=' cannot be applied to operands of type 'S1' and 'S1' + // (17,16): error CS9340: Operator could not be resolved on operands of type 'S1' and 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1?).operator +=(S1?)' // _ = s1 += s1; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s1 += s1").WithArguments("+=", "S1", "S1").WithLocation(17, 13), + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("S1", "S1", "Extensions1.extension(ref S1?).operator +=(S1?)").WithLocation(17, 16), // (18,43): error CS1620: Argument 1 must be passed with the 'ref' keyword // Extensions1.op_AdditionAssignment(s1, s1); Diagnostic(ErrorCode.ERR_BadArgRef, "s1").WithArguments("1", "ref").WithLocation(18, 43), @@ -22516,18 +22559,18 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (23,13): error CS0019: Operator '+=' cannot be applied to operands of type 'S1' and 'S2' + // (23,16): error CS9340: Operator could not be resolved on operands of type 'S1' and 'S2'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator +=(S2)' // _ = s1 += s2; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s1 += s2").WithArguments("+=", "S1", "S2").WithLocation(23, 13), - // (25,13): error CS0019: Operator '+=' cannot be applied to operands of type 'S1' and 'S1' + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("S1", "S2", "Extensions1.extension(ref S2).operator +=(S2)").WithLocation(23, 16), + // (25,16): error CS9340: Operator could not be resolved on operands of type 'S1' and 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator +=(S2)' // _ = s1 += s1; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s1 += s1").WithArguments("+=", "S1", "S1").WithLocation(25, 13), + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("S1", "S1", "Extensions1.extension(ref S2).operator +=(S2)").WithLocation(25, 16), // (26,47): error CS1503: Argument 1: cannot convert from 'ref S1' to 'ref S2' // Extensions1.op_AdditionAssignment(ref s1, s1); Diagnostic(ErrorCode.ERR_BadArgType, "s1").WithArguments("1", "ref S1", "ref S2").WithLocation(26, 47), - // (29,13): error CS0019: Operator '+=' cannot be applied to operands of type 'S1?' and 'S1?' + // (29,16): error CS9340: Operator could not be resolved on operands of type 'S1?' and 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator +=(S2)' // _ = s3 += s3; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s3 += s3").WithArguments("+=", "S1?", "S1?").WithLocation(29, 13), + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("S1?", "S1?", "Extensions1.extension(ref S2).operator +=(S2)").WithLocation(29, 16), // (30,47): error CS1503: Argument 1: cannot convert from 'ref S1?' to 'ref S2' // Extensions1.op_AdditionAssignment(ref s3, s3); Diagnostic(ErrorCode.ERR_BadArgType, "s3").WithArguments("1", "ref S1?", "ref S2").WithLocation(30, 47), @@ -22603,12 +22646,12 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,9): error CS0019: Operator '+=' cannot be applied to operands of type 'S1' and 'int' + // (17,12): error CS9340: Operator could not be resolved on operands of type 'S1' and 'int'. The closest inapplicable candidate is 'Extensions1.extension(object).operator +=(int)' // s1 += 1; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s1 += 1").WithArguments("+=", "S1", "int").WithLocation(17, 9), - // (21,9): error CS0019: Operator '+=' cannot be applied to operands of type 'S1?' and 'int' + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("S1", "int", "Extensions1.extension(object).operator +=(int)").WithLocation(17, 12), + // (21,12): error CS9340: Operator could not be resolved on operands of type 'S1?' and 'int'. The closest inapplicable candidate is 'Extensions1.extension(object).operator +=(int)' // s2 += 1; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s2 += 1").WithArguments("+=", "S1?", "int").WithLocation(21, 9) + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("S1?", "int", "Extensions1.extension(object).operator +=(int)").WithLocation(21, 12) ); } @@ -22815,9 +22858,9 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,13): error CS0019: Operator '+=' cannot be applied to operands of type 'int[]' and 'int' + // (17,16): error CS9340: Operator could not be resolved on operands of type 'int[]' and 'int'. The closest inapplicable candidate is 'Extensions1.extension(ref Span).operator +=(int)' // _ = a1 += 1; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "a1 += 1").WithArguments("+=", "int[]", "int").WithLocation(17, 13), + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("int[]", "int", "Extensions1.extension(ref System.Span).operator +=(int)").WithLocation(17, 16), // (18,47): error CS1503: Argument 1: cannot convert from 'ref int[]' to 'ref System.Span' // Extensions1.op_AdditionAssignment(ref a1, 1); Diagnostic(ErrorCode.ERR_BadArgType, "a1").WithArguments("1", "ref int[]", "ref System.Span").WithLocation(18, 47) @@ -23156,9 +23199,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,13): error CS0019: Operator '+=' cannot be applied to operands of type 'S1' and 'int' + // (17,16): error CS9340: Operator could not be resolved on operands of type 'S1' and 'int'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator +=(int)' // _ = s1 += 1; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s1 += 1").WithArguments("+=", "S1", "int").WithLocation(17, 13) + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("S1", "int", "Extensions1.extension(ref S1).operator +=(int)").WithLocation(17, 16) ); } @@ -23796,14 +23839,25 @@ static void Main() """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); +#if DEBUG + comp.VerifyEmitDiagnostics( + // (35,16): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(C1).operator -(C1, C1)' and 'Extensions1.extension(C1).operator -(C1, C1)' + // _ = c1 -= c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "-=").WithArguments("Extensions2.extension(C1).operator -(C1, C1)", "Extensions1.extension(C1).operator -(C1, C1)").WithLocation(35, 16), + // (39,20): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator checked -(C1, C1)' and 'Extensions2.extension(C1).operator -(C1, C1)' + // _ = c1 -= c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "-=").WithArguments("Extensions1.extension(C1).operator checked -(C1, C1)", "Extensions2.extension(C1).operator -(C1, C1)").WithLocation(39, 20) + ); +#else comp.VerifyEmitDiagnostics( - // (35,13): error CS0034: Operator '-=' is ambiguous on operands of type 'C1' and 'C1' + // (35,16): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator -(C1, C1)' and 'Extensions2.extension(C1).operator -(C1, C1)' // _ = c1 -= c1; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "c1 -= c1").WithArguments("-=", "C1", "C1").WithLocation(35, 13), - // (39,17): error CS0034: Operator '-=' is ambiguous on operands of type 'C1' and 'C1' + Diagnostic(ErrorCode.ERR_AmbigOperator, "-=").WithArguments("Extensions1.extension(C1).operator -(C1, C1)", "Extensions2.extension(C1).operator -(C1, C1)").WithLocation(35, 16), + // (39,20): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator checked -(C1, C1)' and 'Extensions2.extension(C1).operator -(C1, C1)' // _ = c1 -= c1; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "c1 -= c1").WithArguments("-=", "C1", "C1").WithLocation(39, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-=").WithArguments("Extensions1.extension(C1).operator checked -(C1, C1)", "Extensions2.extension(C1).operator -(C1, C1)").WithLocation(39, 20) ); +#endif var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); @@ -27367,9 +27421,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (22,9): error CS0019: Operator '+=' cannot be applied to operands of type 'S2' and 'S2' + // (22,12): error CS9340: Operator could not be resolved on operands of type 'S2' and 'S2'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator +=(int)' // s2 += s2; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s2 += s2").WithArguments("+=", "S2", "S2").WithLocation(22, 9) + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("S2", "S2", "Extensions1.extension(ref S2).operator +=(int)").WithLocation(22, 12) ); var tree = comp.SyntaxTrees.First(); @@ -27471,9 +27525,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (22,9): error CS0019: Operator '+=' cannot be applied to operands of type 'S2' and 'S2' + // (22,12): error CS9340: Operator could not be resolved on operands of type 'S2' and 'S2'. The closest inapplicable candidate is 'Extensions1.extension(S2).operator +(S2, int)' // s2 += s2; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "s2 += s2").WithArguments("+=", "S2", "S2").WithLocation(22, 9) + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("S2", "S2", "Extensions1.extension(S2).operator +(S2, int)").WithLocation(22, 12) ); var tree = comp.SyntaxTrees.First(); @@ -27526,9 +27580,9 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (29,9): error CS0034: Operator '+=' is ambiguous on operands of type 'S2' and 'S2' + // (29,12): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions1.extension(S2).operator +(S2, C1)' and 'Extensions1.extension(S2).operator +(S2, C2)' // s2 += s2; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "s2 += s2").WithArguments("+=", "S2", "S2").WithLocation(29, 9) + Diagnostic(ErrorCode.ERR_AmbigOperator, "+=").WithArguments("Extensions1.extension(S2).operator +(S2, C1)", "Extensions1.extension(S2).operator +(S2, C2)").WithLocation(29, 12) ); var tree = comp.SyntaxTrees.First(); @@ -27692,9 +27746,9 @@ static class E2 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (1,5): error CS0034: Operator '+' is ambiguous on operands of type 'S' and 'S' + // (1,13): error CS9339: Operator resolution is ambiguous between the following members: 'E1.extension(S).operator +(S, S)' and 'E2.extension(in S).operator +(S, S)' // _ = new S() + new S(); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new S() + new S()").WithArguments("+", "S", "S").WithLocation(1, 5)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("E1.extension(S).operator +(S, S)", "E2.extension(in S).operator +(S, S)").WithLocation(1, 13)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); @@ -27771,9 +27825,9 @@ static class E2 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (4,5): error CS0034: Operator '+' is ambiguous on operands of type 'C' and 'C' + // (4,13): error CS9339: Operator resolution is ambiguous between the following members: 'E1.extension(C).operator +(C, C)' and 'E2.extension(C).operator +(C, C)' // _ = new C() + new C(); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new C() + new C()").WithArguments("+", "C", "C").WithLocation(4, 5)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("N1.E1.extension(C).operator +(C, C)", "N2.E2.extension(C).operator +(C, C)").WithLocation(4, 13)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -27859,9 +27913,9 @@ static class E2 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (4,5): error CS0034: Operator '+' is ambiguous on operands of type 'C' and 'C' + // (4,13): error CS9339: Operator resolution is ambiguous between the following members: 'E1.extension(C).operator +(C, C)' and 'E2.extension(C).operator +(C, C)' // _ = new C() + new C(); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new C() + new C()").WithArguments("+", "C", "C").WithLocation(4, 5)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("N1.E1.extension(C).operator +(C, C)", "N2.E2.extension(C).operator +(C, C)").WithLocation(4, 13)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -28278,9 +28332,9 @@ static class E2 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,1): error CS0034: Operator '+=' is ambiguous on operands of type 'C' and 'C' + // (5,3): error CS9339: Operator resolution is ambiguous between the following members: 'E1.extension(C).operator +(C, C)' and 'E2.extension(C).operator +(C, C)' // c += new C(); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "c += new C()").WithArguments("+=", "C", "C").WithLocation(5, 1)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+=").WithArguments("N1.E1.extension(C).operator +(C, C)", "N2.E2.extension(C).operator +(C, C)").WithLocation(5, 3)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -28441,9 +28495,9 @@ static class E2 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,5): error CS0035: Operator '+' is ambiguous on an operand of type 'C' + // (5,5): error CS9339: Operator resolution is ambiguous between the following members: 'E1.extension(C).operator +(C)' and 'E2.extension(C).operator +(C)' // _ = +c; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "+c").WithArguments("+", "C").WithLocation(5, 5)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("N1.E1.extension(C).operator +(C)", "N2.E2.extension(C).operator +(C)").WithLocation(5, 5)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); @@ -28608,9 +28662,9 @@ static class E2 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,5): error CS0035: Operator '++' is ambiguous on an operand of type 'C' + // (5,6): error CS9339: Operator resolution is ambiguous between the following members: 'E1.extension(C).operator ++(C)' and 'E2.extension(C).operator ++(C)' // _ = c++; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "c++").WithArguments("++", "C").WithLocation(5, 5)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "++").WithArguments("N1.E1.extension(C).operator ++(C)", "N2.E2.extension(C).operator ++(C)").WithLocation(5, 6)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -28735,9 +28789,9 @@ static class E2 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (5,5): error CS0035: Operator '++' is ambiguous on an operand of type 'C' + // (5,5): error CS9339: Operator resolution is ambiguous between the following members: 'E1.extension(C).operator ++(C)' and 'E2.extension(C).operator ++(C)' // _ = ++c; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "++c").WithArguments("++", "C").WithLocation(5, 5)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "++").WithArguments("N1.E1.extension(C).operator ++(C)", "N2.E2.extension(C).operator ++(C)").WithLocation(5, 5)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -29412,5 +29466,750 @@ .method public hidebysig static void op_IncrementAssignment ( valuetype S s ) ci // _ = ++s; Diagnostic(ErrorCode.ERR_BadUnaryOp, "++s").WithArguments("++", "S").WithLocation(4, 5)); } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_01() + { + // inner scope has inapplicable operator, outter scope too + var src = """ +using N; + +I1 i1 = null; +i1 += 42; + +public static class E1 +{ + extension(I1) + { + public static I1 operator +(I1 i1, I2 i2) => throw null; + } +} + +namespace N +{ + public static class E2 + { + extension(I1) + { + public static I1 operator +(I1 i1, I2 i2) => throw null; + } + } +} + +public interface I1 { } +public interface I2 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,4): error CS9340: Operator 'I1' could not be resolved on operands of type 'I1' and 'int'. The closest inapplicable candidate is 'E1.extension(I1).operator +(I1, I2)' + // i1 += 42; + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("I1", "int", "E1.extension(I1).operator +(I1, I2)").WithLocation(4, 4) + ); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_02() + { + // inner scope has inapplicable operator, outer scope has two applicable/worse operators + var src = """ +using N; + +I1 i1 = null; +i1 += 42; + +public static class E1 +{ + extension(I1) + { + public static I1 operator +(I1 i1, I2 i2) => throw null; + } +} + +namespace N +{ + public static class E2 + { + extension(I1) + { + public static I1 operator +(I1 i1, int i) => throw null; + } + } + public static class E3 + { + extension(I1) + { + public static I1 operator +(I1 i1, int i) => throw null; + } + } +} + +public interface I1 { } +public interface I2 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,4): error CS9340: Operator 'I1' could not be resolved on operands of type 'I1' and 'int'. The closest inapplicable candidate is 'E1.extension(I1).operator +(I1, I2)' + // i1 += 42; + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("I1", "int", "E1.extension(I1).operator +(I1, I2)").WithLocation(4, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_03() + { + // inner scope has two inapplicable operators, outer scope has two applicable/worse operators + var src = """ +using N; + +I1 i1 = null; +i1 += 42; + +public static class E1 +{ + extension(I1) + { + public static I1 operator +(I1 i1, I2 i2) => throw null; + } +} + +public static class E2 +{ + extension(I1) + { + public static I1 operator +(I1 i1, I2 i2) => throw null; + } +} + +namespace N +{ + public static class E3 + { + extension(I1) + { + public static I1 operator +(I1 i1, int i) => throw null; + } + } + public static class E4 + { + extension(I1) + { + public static I1 operator +(I1 i1, int i) => throw null; + } + } +} + +public interface I1 { } +public interface I2 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,1): error CS0034: Operator '+=' is ambiguous on operands of type 'I1' and 'int' + // i1 += 42; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "i1 += 42").WithArguments("+=", "I1", "int").WithLocation(4, 1)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_04() + { + // outer scope has inapplicable operator, inner scope has two applicable/worse operators + var src = """ +using N; + +I1 i1 = null; +i1 += 42; + +public static class E1 +{ + extension(I1) + { + public static I1 operator +(I1 i1, int i) => throw null; + } +} + +public static class E2 +{ + extension(I1) + { + public static I1 operator +(I1 i1, int i) => throw null; + } +} + +namespace N +{ + public static class E3 + { + extension(I1) + { + public static I1 operator +(I1 i1, I2 i2) => throw null; + } + } +} + +public interface I1 { } +public interface I2 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,4): error CS9339: Operator resolution is ambiguous between the following members: 'E1.extension(I1).operator +(I1, int)' and 'E2.extension(I1).operator +(I1, int)' + // i1 += 42; + Diagnostic(ErrorCode.ERR_AmbigOperator, "+=").WithArguments("E1.extension(I1).operator +(I1, int)", "E2.extension(I1).operator +(I1, int)").WithLocation(4, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_05() + { + // single inapplicable instance operator and single inapplicable static operator + var src = """ +I1 i1 = null; +i1 += 42; + +public static class E1 +{ + extension(I1 i1) + { + public void operator +=(I2 i2) => throw null; + public static I1 operator +(I1 i2, I2 i3) => throw null; + } +} + +public interface I1 { } +public interface I2 { } +"""; + + var comp = CreateCompilation([src, CompilerFeatureRequiredAttribute]); + comp.VerifyEmitDiagnostics( + // (2,4): error CS9340: Operator 'I1' could not be resolved on operands of type 'I1' and 'int'. The closest inapplicable candidate is 'E1.extension(I1).operator +=(I2)' + // i1 += 42; + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("I1", "int", "E1.extension(I1).operator +=(I2)").WithLocation(2, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_06() + { + // single inapplicable instance operator + var src = """ +I1 i1 = null; +i1 += 42; + +public static class E1 +{ + extension(I1 i1) + { + public void operator +=(I2 i2) => throw null; + } +} + +public interface I1 { } +public interface I2 { } +"""; + + var comp = CreateCompilation([src, CompilerFeatureRequiredAttribute]); + comp.VerifyEmitDiagnostics( + // (2,4): error CS9340: Operator 'I1' could not be resolved on operands of type 'I1' and 'int'. The closest inapplicable candidate is 'E1.extension(I1).operator +=(I2)' + // i1 += 42; + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("I1", "int", "E1.extension(I1).operator +=(I2)").WithLocation(2, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_07() + { + // single inapplicable static operator + var src = """ +I1 i1 = null; +i1 += 42; + +public static class E1 +{ + extension(I1 i1) + { + public static I1 operator +(I1 i2, I2 i3) => throw null; + } +} + +public interface I1 { } +public interface I2 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,4): error CS9340: Operator 'I1' could not be resolved on operands of type 'I1' and 'int'. The closest inapplicable candidate is 'E1.extension(I1).operator +(I1, I2)' + // i1 += 42; + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("I1", "int", "E1.extension(I1).operator +(I1, I2)").WithLocation(2, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_08() + { + // inapplicable static operator and irrelevant static operator (not compatible with receiver) + var src = """ +I1 i1 = null; +i1 += 42; + +public static class E1 +{ + extension(I1) + { + public static I1 operator +(I1 i2, I2 i3) => throw null; + } + + extension(I2) + { + public static I2 operator +(I2 i, I2 j) => throw null; + } +} + +public interface I1 { } +public interface I2 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,4): error CS9340: Operator 'I1' could not be resolved on operands of type 'I1' and 'int'. The closest inapplicable candidate is 'E1.extension(I1).operator +(I1, I2)' + // i1 += 42; + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("I1", "int", "E1.extension(I1).operator +(I1, I2)").WithLocation(2, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_09() + { + // inapplicable non-extension static operator + var src = """ +C1 c1 = null; +c1 += new C3(); + +public class C1 +{ + public static C1 operator +(C1 c1, C2 c2) => throw null; +} + +public class C2 { } +public class C3 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'C3' + // c1 += new C3(); + Diagnostic(ErrorCode.ERR_BadBinaryOps, "c1 += new C3()").WithArguments("+=", "C1", "C3").WithLocation(2, 1)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_10() + { + // inapplicable non-extension static operator, single inapplicable extension static operator + var src = """ +C1 c1 = null; +c1 += new C3(); + +public class C1 +{ + public static C1 operator +(C1 c1, C2 c2) => throw null; +} + +public static class E1 +{ + extension(C1) + { + public static C1 operator +(C1 c1, C2 c2) => throw null; + } +} + +public class C2 { } +public class C3 { } +"""; + + // Note: GetUserDefinedOperators clears its results when no candidate was applicable. That's why we report the extension operator rather than the equivalent non-extension operator + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,4): error CS9340: Operator 'C1' could not be resolved on operands of type 'C1' and 'C3'. The closest inapplicable candidate is 'E1.extension(C1).operator +(C1, C2)' + // c1 += new C3(); + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("C1", "C3", "E1.extension(C1).operator +(C1, C2)").WithLocation(2, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_11() + { + // inapplicable non-extension static operator, two inapplicable extension static operators + var src = """ +C1 c1 = null; +c1 += new C3(); + +public class C1 +{ + public static C1 operator +(C1 c1, C2 c2) => throw null; +} + +public static class E1 +{ + extension(C1) + { + public static C1 operator +(C1 c1, C2 c2) => throw null; + } +} + +public static class E2 +{ + extension(C1) + { + public static C1 operator +(C1 c1, C2 c2) => throw null; + } +} + +public class C2 { } +public class C3 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'C3' + // c1 += new C3(); + Diagnostic(ErrorCode.ERR_BadBinaryOps, "c1 += new C3()").WithArguments("+=", "C1", "C3").WithLocation(2, 1)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_12() + { + // inapplicable non-extension static operator, two applicable extension static operators + var src = """ +C1 c1 = null; +c1 += new C3(); + +public class C1 +{ + public static C1 operator +(C1 c1, C2 c2) => throw null; +} + +public static class E1 +{ + extension(C1) + { + public static C1 operator +(C1 c1, C3 c3) => throw null; + } +} + +public static class E2 +{ + extension(C1) + { + public static C1 operator +(C1 c1, C3 c3) => throw null; + } +} + +public class C2 { } +public class C3 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,4): error CS9339: Operator resolution is ambiguous between the following members: 'E1.extension(C1).operator +(C1, C3)' and 'E2.extension(C1).operator +(C1, C3)' + // c1 += new C3(); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+=").WithArguments("E1.extension(C1).operator +(C1, C3)", "E2.extension(C1).operator +(C1, C3)").WithLocation(2, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_13() + { + // two applicable non-extension static operators + var src = """ +C1 c1 = null; +c1 += new C2(); + +public class C1 +{ + public static C1 operator +(C1 c1, C2 c2) => throw null; +} + +public class C2 +{ + public static C1 operator +(C1 c1, C2 c2) => throw null; +} + +public class C3 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,4): error CS9339: Operator resolution is ambiguous between the following members: 'C1.operator +(C1, C2)' and 'C2.operator +(C1, C2)' + // c1 += new C2(); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+=").WithArguments("C1.operator +(C1, C2)", "C2.operator +(C1, C2)").WithLocation(2, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_14() + { + // two applicable non-extension static operators, single applicable extension static operator + var src = """ +C1 c1 = null; +c1 += new C2(); + +public class C1 +{ + public static C1 operator +(C1 c1, C2 c2) => throw null; +} + +public class C2 +{ + public static C1 operator +(C1 c1, C2 c2) => throw null; +} + +public static class E +{ + extension(C1) + { + public static C1 operator +(C1 c1, C2 c2) => throw null; + } +} + +public class C3 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,4): error CS9339: Operator resolution is ambiguous between the following members: 'C1.operator +(C1, C2)' and 'C2.operator +(C1, C2)' + // c1 += new C2(); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+=").WithArguments("C1.operator +(C1, C2)", "C2.operator +(C1, C2)").WithLocation(2, 4)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_15() + { + // two inapplicable non-extension static operators + var src = """ +C1 c1 = null; +c1 += new C2(); + +public class C1 +{ + public static C1 operator +(C1 c1, C3 c3) => throw null; +} + +public class C2 +{ + public static C1 operator +(C3 c3, C2 c2) => throw null; +} + +public class C3 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'C2' + // c1 += new C2(); + Diagnostic(ErrorCode.ERR_BadBinaryOps, "c1 += new C2()").WithArguments("+=", "C1", "C2").WithLocation(2, 1)); + } + + [Fact] + public void ReportDiagnostics_CompoundAssignment_16() + { + // two inapplicable non-extension static operators, second operand type is integer + var src = """ +C1 c1 = null; +c1 += 42; + +public class C1 +{ + public static C1 operator +(C1 c1, C3 c3) => throw null; +} + +public class C2 +{ + public static C1 operator +(C3 c3, C2 c2) => throw null; +} + +public class C3 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int' + // c1 += 42; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "c1 += 42").WithArguments("+=", "C1", "int").WithLocation(2, 1)); + } + + [Fact] + public void ReportDiagnostics_Binary_01() + { + var src = """ +_ = new C1() + new C2(); + +public static class E1 +{ + extension(C1) + { + public static C1 operator +(C1 c1, C2 c2) => throw null; + } +} + +public static class E2 +{ + extension(C1) + { + public static C1 operator +(C1 c1, C2 c2) => throw null; + } +} + +public class C1 { } +public class C2 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS9342: Operator resolution is ambiguous between the following members: 'E1.extension(C1).operator +(C1, C2)' and 'E2.extension(C1).operator +(C1, C2)' + // _ = new C1() + new C2(); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("E1.extension(C1).operator +(C1, C2)", "E2.extension(C1).operator +(C1, C2)").WithLocation(1, 14)); + } + + [Fact] + public void ReportDiagnostics_Binary_02() + { + var src = """ +_ = new C1() == new C2(); + +public static class E1 +{ + extension(C1) + { + public static bool operator ==(C1 c1, C2 c2) => throw null; + public static bool operator !=(C1 c1, C2 c2) => throw null; + } +} + +public static class E2 +{ + extension(C1) + { + public static bool operator ==(C1 c1, C2 c2) => throw null; + public static bool operator !=(C1 c1, C2 c2) => throw null; + } +} + +public class C1 { } +public class C2 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS0034: Operator '==' is ambiguous on operands of type 'C1' and 'C2' + // _ = new C1() == new C2(); + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new C1() == new C2()").WithArguments("==", "C1", "C2").WithLocation(1, 5)); + } + + [Fact] + public void ReportDiagnostics_Binary_03() + { + var src = """ +_ = null == null; +_ = null == default; +_ = null == (() => { }); +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,5): error CS0034: Operator '==' is ambiguous on operands of type '' and 'default' + // _ = null == default; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "null == default").WithArguments("==", "", "default").WithLocation(2, 5)); + } + + [Fact] + public void ReportDiagnostics_Unary_01() + { + var src = """ +C1 c1 = null; +_ = +c1; + +public static class E1 +{ + extension(C1) + { + public static C1 operator +(C1 c1) => throw null; + } +} + +public static class E2 +{ + extension(C1) + { + public static C1 operator +(C1 c1) => throw null; + } +} + +public class C1 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,5): error CS9342: Operator resolution is ambiguous between the following members: 'E1.extension(C1).operator +(C1)' and 'E2.extension(C1).operator +(C1)' + // _ = +c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("E1.extension(C1).operator +(C1)", "E2.extension(C1).operator +(C1)").WithLocation(2, 5)); + } + + [Fact] + public void ReportDiagnostics_Increment_01() + { + var src = """ +C1 c1 = null; +_ = ++c1; + +public static class E1 +{ + extension(C1) + { + public static C1 operator ++(C1 c1) => throw null; + } +} + +public static class E2 +{ + extension(C1) + { + public static C1 operator ++(C1 c1) => throw null; + } +} + +public class C1 { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,5): error CS9342: Operator resolution is ambiguous between the following members: 'E1.extension(C1).operator ++(C1)' and 'E2.extension(C1).operator ++(C1)' + // _ = ++c1; + Diagnostic(ErrorCode.ERR_AmbigOperator, "++").WithArguments("E1.extension(C1).operator ++(C1)", "E2.extension(C1).operator ++(C1)").WithLocation(2, 5)); + } + + [Fact] + public void ReportDiagnostics_Increment_02() + { + var src = """ +C1 c1 = null; +_ = ++c1; + +public static class E1 +{ + extension(C1 c1) + { + public void operator ++() => throw null; + } +} + +public static class E2 +{ + extension(C1 c1) + { + public void operator ++() => throw null; + } +} + +public class C1 { } +"""; + + var comp = CreateCompilation([src, CompilerFeatureRequiredAttribute]); + comp.VerifyEmitDiagnostics( + // (2,5): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(C1).operator ++()' and 'E2.extension(C1).operator ++()' + // _ = ++c1; + Diagnostic(ErrorCode.ERR_AmbigCall, "++").WithArguments("E1.extension(C1).operator ++()", "E2.extension(C1).operator ++()").WithLocation(2, 5)); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 76d5531d43ec0..8a96317f9103b 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -35986,9 +35986,9 @@ static class E2 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (1,5): error CS0034: Operator '+' is ambiguous on operands of type 'C' and 'C' + // (1,13): error CS9342: Operator resolution is ambiguous between the following members: 'E1.extension(C).operator +(C, C)' and 'E2.extension(C).operator +(C, C)' // _ = new C() + new C(); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new C() + new C()").WithArguments("+", "C", "C").WithLocation(1, 5)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("E1.extension(C).operator +(C, C)", "E2.extension(C).operator +(C, C)").WithLocation(1, 13)); } [Fact] @@ -36102,9 +36102,9 @@ static class E3 """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (1,5): error CS0034: Operator '+' is ambiguous on operands of type 'C' and 'C' + // (1,13): error CS9342: Operator resolution is ambiguous between the following members: 'E1.extension(C).operator +(C, C)' and 'E2.extension(C).operator +(C, C)' // _ = new C() + new C(); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new C() + new C()").WithArguments("+", "C", "C").WithLocation(1, 5)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("E1.extension(C).operator +(C, C)", "E2.extension(C).operator +(C, C)").WithLocation(1, 13)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs b/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs index 01e4a6498e345..6f46aa94fd1c9 100644 --- a/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs @@ -16950,9 +16950,9 @@ static void Main() var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); comp2.VerifyDiagnostics( - // (7,9): error CS0019: Operator '+=' cannot be applied to operands of type 'C1' and 'int' + // (7,11): error CS9340: Operator cannot be applied on operands of type 'C1' and 'int'. The closest inapplicable candidate is 'C1.operator +=(params int[])' // x += 1; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "x += 1").WithArguments("+=", "C1", "int").WithLocation(7, 9) + Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("C1", "int", "C1.operator +=(params int[])").WithLocation(7, 11) ); } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IBinaryOperatorExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IBinaryOperatorExpression.cs index 45ee0a5aad344..a1504c40d812f 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IBinaryOperatorExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IBinaryOperatorExpression.cs @@ -4415,30 +4415,29 @@ class B3 Predecessors: [B0] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'result = a && b;') - Expression: + Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Object, IsInvalid) (Syntax: 'result = a && b') - Left: + Left: IParameterReferenceOperation: result (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'result') - Right: + Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'a && b') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) - Operand: + Operand: IBinaryOperation (BinaryOperatorKind.ConditionalAnd) (OperationKind.Binary, Type: ?, IsInvalid) (Syntax: 'a && b') - Left: - IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: C.B3, IsInvalid) (Syntax: 'a') - Right: - IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: C.B2, IsInvalid) (Syntax: 'b') - + Left: + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: C.B3) (Syntax: 'a') + Right: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: C.B2) (Syntax: 'b') Next (Regular) Block[B2] Block[B2] - Exit Predecessors: [B1] Statements (0) "; var expectedDiagnostics = new[] { - // file.cs(6,18): error CS0034: Operator '&&' is ambiguous on operands of type 'C.B3' and 'C.B2' + // (6,20): error CS9342: Operator resolution is ambiguous between the following members: 'C.B3.operator &(C.B3, C.B2)' and 'C.B2.operator &(C.B3, C.B2)' // result = a && b; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "a && b").WithArguments("&&", "C.B3", "C.B2").WithLocation(6, 18) + Diagnostic(ErrorCode.ERR_AmbigOperator, "&&").WithArguments("C.B3.operator &(C.B3, C.B2)", "C.B2.operator &(C.B3, C.B2)").WithLocation(6, 20) }; VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 0029ab5e4d20b..73a337439650c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -10779,12 +10779,12 @@ static void Main() var expectedDiagnostics = new[] { - // (10,13): error CS0034: Operator '+' is ambiguous on operands of type 'C' and 'method group' + // (10,15): error CS9342: Operator resolution is ambiguous between the following members: 'C.operator +(C, Delegate)' and 'C.operator +(C, Expression)' // _ = c + Main; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "c + Main").WithArguments("+", "C", "method group").WithLocation(10, 13), - // (11,13): error CS0034: Operator '+' is ambiguous on operands of type 'C' and 'lambda expression' + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("C.operator +(C, System.Delegate)", "C.operator +(C, System.Linq.Expressions.Expression)").WithLocation(10, 15), + // (11,15): error CS9342: Operator resolution is ambiguous between the following members: 'C.operator +(C, Delegate)' and 'C.operator +(C, Expression)' // _ = c + (() => 1); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "c + (() => 1)").WithArguments("+", "C", "lambda expression").WithLocation(11, 13) + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("C.operator +(C, System.Delegate)", "C.operator +(C, System.Linq.Expressions.Expression)").WithLocation(11, 15) }; comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics(expectedDiagnostics); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 846052e3cde66..d599c999c6888 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -146679,12 +146679,12 @@ static void M(T x, T y) where T : I<(int a, int b)>, I2 var compilation1 = CreateCompilation(source, parseOptions: TestOptions.Regular, targetFramework: TargetFramework.NetCoreApp); compilation1.VerifyDiagnostics( - // (13,17): error CS0034: Operator '+' is ambiguous on operands of type 'T' and 'T' + // (13,19): error CS9342: Operator resolution is ambiguous between the following members: 'I<(int a, int b)>.operator +(I<(int a, int b)>, I<(int a, int b)>)' and 'I<(int c, int d)>.operator +(I<(int c, int d)>, I<(int c, int d)>)' // var z = x + y; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + y").WithArguments("+", "T", "T").WithLocation(13, 17), - // (14,13): error CS0034: Operator '+' is ambiguous on operands of type 'T' and 'T' + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("I<(int a, int b)>.operator +(I<(int a, int b)>, I<(int a, int b)>)", "I<(int c, int d)>.operator +(I<(int c, int d)>, I<(int c, int d)>)").WithLocation(13, 19), + // (14,15): error CS9342: Operator resolution is ambiguous between the following members: 'I<(int a, int b)>.operator +(I<(int a, int b)>, I<(int a, int b)>)' and 'I<(int c, int d)>.operator +(I<(int c, int d)>, I<(int c, int d)>)' // z = y + x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "y + x").WithArguments("+", "T", "T").WithLocation(14, 13) + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("I<(int a, int b)>.operator +(I<(int a, int b)>, I<(int a, int b)>)", "I<(int c, int d)>.operator +(I<(int c, int d)>, I<(int c, int d)>)").WithLocation(14, 15) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs index 4760240803eb7..5ba14eb7e553e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OperatorTests.cs @@ -1335,21 +1335,21 @@ public static void Main() "; string expectedOperationTree = @" IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: ?, IsInvalid) (Syntax: 'new C() + new B()') - Left: - IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'new C()') + Left: + IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') Arguments(0) - Initializer: + Initializer: null - Right: - IObjectCreationOperation (Constructor: B..ctor()) (OperationKind.ObjectCreation, Type: B, IsInvalid) (Syntax: 'new B()') + Right: + IObjectCreationOperation (Constructor: B..ctor()) (OperationKind.ObjectCreation, Type: B) (Syntax: 'new B()') Arguments(0) - Initializer: + Initializer: null "; var expectedDiagnostics = new DiagnosticDescription[] { - // CS0034: Operator '+' is ambiguous on operands of type 'C' and 'B' + // (16,33): error CS9342: Operator resolution is ambiguous between the following members: 'C.operator +(C, B)' and 'B.operator +(C, B)' // B b = /**/new C() + new B()/**/; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new C() + new B()").WithArguments("+", "C", "B").WithLocation(16, 25) + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("C.operator +(C, B)", "B.operator +(C, B)").WithLocation(16, 33) }; VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); @@ -1541,15 +1541,15 @@ static void Main() "; string expectedOperationTree = @" IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: ?, IsInvalid) (Syntax: 'x + y') - Left: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: D.C, IsInvalid) (Syntax: 'x') - Right: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: D.C, IsInvalid) (Syntax: 'y') + Left: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: D.C) (Syntax: 'x') + Right: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: D.C) (Syntax: 'y') "; var expectedDiagnostics = new DiagnosticDescription[] { - // CS0034: Operator '+' is ambiguous on operands of type 'D.C' and 'D.C' + // (16,29): error CS9342: Operator resolution is ambiguous between the following members: 'D.C.operator +(D.C, D.C)' and 'D.C.operator +(D.C, D.C)' // var z = /**/x + y/**/; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + y").WithArguments("+", "D.C", "D.C").WithLocation(16, 27) + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("D.C.operator +(D.C, D.C)", "D.C.operator +(D.C, D.C)").WithLocation(16, 29) }; VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); @@ -6163,9 +6163,9 @@ static void Main() "; CompileAndVerify(source1, expectedOutput: "1"); CreateCompilation(source2).VerifyDiagnostics( -// (16,9): error CS0034: Operator '==' is ambiguous on operands of type 'S?' and '' -// if (s == null) s = default(S); -Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "s == null").WithArguments("==", "S?", "")); + // (16,11): error CS9342: Operator resolution is ambiguous between the following members: 'S.operator ==(S?, decimal?)' and 'S.operator ==(S?, double?)' + // if (s == null) s = default(S); + Diagnostic(ErrorCode.ERR_AmbigOperator, "==").WithArguments("S.operator ==(S?, decimal?)", "S.operator ==(S?, double?)").WithLocation(16, 11)); } [WorkItem(543432, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543432")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs index addea1aa9099b..eae5318d6f701 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs @@ -65,9 +65,9 @@ public void BinaryOperatorOverloads() var source = builder.ToString(); var comp = CreateCompilationWithMscorlib40AndSystemCore(source); comp.VerifyDiagnostics( - // (3,29): error CS0034: Operator '+' is ambiguous on operands of type 'C' and '' + // (3,31): error CS9342: Operator resolution is ambiguous between the following members: 'C.operator +(C, C0)' and 'C.operator +(C, C1)' // static object F(C x) => x + null; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + null").WithArguments("+", "C", "").WithLocation(3, 29)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("C.operator +(C, C0)", "C.operator +(C, C1)").WithLocation(3, 31)); } [ConditionalFact(typeof(IsRelease))] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs index 9f071505b90eb..041c8e9ce3c4f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs @@ -10586,9 +10586,9 @@ static void Main() Console.WriteLine(a + b); } }").VerifyDiagnostics( - // (15,27): error CS0034: Operator '+' is ambiguous on operands of type 'Test' and 'Test' + // (15,29): error CS9342: Operator resolution is ambiguous between the following members: 'Test.operator +(in Test, Test)' and 'Test.operator +(Test, in Test)' // Console.WriteLine(a + b); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "a + b").WithArguments("+", "Test", "Test").WithLocation(15, 27)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("Test.operator +(in Test, Test)", "Test.operator +(Test, in Test)").WithLocation(15, 29)); } [Fact] @@ -10611,9 +10611,9 @@ static void Main() Console.WriteLine(a + b); } }").VerifyDiagnostics( - // (15,27): error CS0034: Operator '+' is ambiguous on operands of type 'Test' and 'Test' - // Console.WriteLine(a + b); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "a + b").WithArguments("+", "Test", "Test").WithLocation(15, 27)); + // (15,29): error CS9342: Operator resolution is ambiguous between the following members: 'Test.operator +(Test, in Test)' and 'Test.operator +(in Test, Test)' + // Console.WriteLine(a + b); + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("Test.operator +(Test, in Test)", "Test.operator +(in Test, Test)").WithLocation(15, 29)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index a78969af75e32..5567c4536ec09 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -47457,9 +47457,9 @@ static void Main() var expected = new DiagnosticDescription[] { - // (7,17): error CS0035: Operator '-' is ambiguous on an operand of type 'I2' + // (7,17): error CS9342: Operator resolution is ambiguous between the following members: 'I1.operator -(I1)' and 'I3.operator -(I3)' // var y = -x; - Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "-x").WithArguments("-", "I2").WithLocation(7, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("I1.operator -(I1)", "I3.operator -(I3)").WithLocation(7, 17) }; var compilation1 = CreateCompilation(source2 + source1, options: TestOptions.DebugExe, @@ -47670,9 +47670,9 @@ static void Main() var expected = new DiagnosticDescription[] { - // (7,17): error CS0034: Operator '+' is ambiguous on operands of type 'C1' and 'I3' + // (7,26): error CS9342: Operator resolution is ambiguous between the following members: 'C1.operator +(C1, I2)' and 'C1.operator +(C1, I1)' // var y = new C1() + x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new C1() + x").WithArguments("+", "C1", "I3").WithLocation(7, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("C1.operator +(C1, I2)", "C1.operator +(C1, I1)").WithLocation(7, 26) }; var compilation0 = CreateCompilation(source0 + source1 + source2, options: TestOptions.DebugDll, @@ -47803,9 +47803,9 @@ static void Main() var expected = new DiagnosticDescription[] { - // (7,17): error CS0034: Operator '+' is ambiguous on operands of type 'I3' and 'C1' + // (7,19): error CS9342: Operator resolution is ambiguous between the following members: 'C1.operator +(I2, C1)' and 'C1.operator +(I1, C1)' // var y = x + new C1(); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + new C1()").WithArguments("+", "I3", "C1").WithLocation(7, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("C1.operator +(I2, C1)", "C1.operator +(I1, C1)").WithLocation(7, 19) }; var compilation0 = CreateCompilation(source0 + source1 + source2, options: TestOptions.DebugDll, @@ -47928,9 +47928,9 @@ static void Main() var expected = new DiagnosticDescription[] { - // (8,17): error CS0034: Operator '+' is ambiguous on operands of type 'I1' and 'I2' + // (8,19): error CS9342: Operator resolution is ambiguous between the following members: 'I1.operator +(I1, I2)' and 'I2.operator +(I1, I2)' // var z = x + y; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + y").WithArguments("+", "I1", "I2").WithLocation(8, 17), + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("I1.operator +(I1, I2)", "I2.operator +(I1, I2)").WithLocation(8, 19), // (9,13): error CS0019: Operator '+' cannot be applied to operands of type 'I2' and 'I1' // z = y + x; Diagnostic(ErrorCode.ERR_BadBinaryOps, "y + x").WithArguments("+", "I2", "I1").WithLocation(9, 13) @@ -47995,9 +47995,9 @@ static void Main() var expected = new DiagnosticDescription[] { - // (8,17): error CS0034: Operator '+' is ambiguous on operands of type 'I2' and 'I1' + // (8,19): error CS9342: Operator resolution is ambiguous between the following members: 'I2.operator +(I2, I1)' and 'I1.operator +(I2, I1)' // var z = y + x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "y + x").WithArguments("+", "I2", "I1").WithLocation(8, 17), + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("I2.operator +(I2, I1)", "I1.operator +(I2, I1)").WithLocation(8, 19), // (9,13): error CS0019: Operator '+' cannot be applied to operands of type 'I1' and 'I2' // z = x + y; Diagnostic(ErrorCode.ERR_BadBinaryOps, "x + y").WithArguments("+", "I1", "I2").WithLocation(9, 13) @@ -48649,9 +48649,9 @@ static void Main() var expected = new DiagnosticDescription[] { - // (8,17): error CS0034: Operator '+' is ambiguous on operands of type 'I1' and 'I3' + // (8,19): error CS9342: Operator resolution is ambiguous between the following members: 'I1.operator +(I1, I2)' and 'I2.operator +(I1, I2)' // var z = x + y; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + y").WithArguments("+", "I1", "I3").WithLocation(8, 17), + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("I1.operator +(I1, I2)", "I2.operator +(I1, I2)").WithLocation(8, 19), // (9,13): error CS0019: Operator '+' cannot be applied to operands of type 'I3' and 'I1' // z = y + x; Diagnostic(ErrorCode.ERR_BadBinaryOps, "y + x").WithArguments("+", "I3", "I1").WithLocation(9, 13) @@ -48719,9 +48719,9 @@ static void Main() var expected = new DiagnosticDescription[] { - // (8,17): error CS0034: Operator '+' is ambiguous on operands of type 'I3' and 'I1' + // (8,19): error CS9342: Operator resolution is ambiguous between the following members: 'I2.operator +(I2, I1)' and 'I1.operator +(I2, I1)' // var z = y + x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "y + x").WithArguments("+", "I3", "I1").WithLocation(8, 17), + Diagnostic(ErrorCode.ERR_AmbigOperator, "+").WithArguments("I2.operator +(I2, I1)", "I1.operator +(I2, I1)").WithLocation(8, 19), // (9,13): error CS0019: Operator '+' cannot be applied to operands of type 'I1' and 'I3' // z = x + y; Diagnostic(ErrorCode.ERR_BadBinaryOps, "x + y").WithArguments("+", "I1", "I3").WithLocation(9, 13) @@ -48792,9 +48792,9 @@ static void Main() var expected = new DiagnosticDescription[] { - // (7,17): error CS0034: Operator '-' is ambiguous on operands of type 'I2' and 'I2' + // (7,19): error CS9342: Operator resolution is ambiguous between the following members: 'I1.operator -(I1, I1)' and 'I3.operator -(I3, I3)' // var y = x - x; - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x - x").WithArguments("-", "I2", "I2").WithLocation(7, 17) + Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("I1.operator -(I1, I1)", "I3.operator -(I3, I3)").WithLocation(7, 19) }; var compilation1 = CreateCompilation(source2 + source1, options: TestOptions.DebugExe, From 6a9164bab70a9778858d3c0d1fb6872ded13c866 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 29 Oct 2025 12:29:21 -0700 Subject: [PATCH 2/6] Address feedback --- .../Binder.OperatorResolutionForReporting.cs | 51 ++++++++++++------- .../CSharp/Portable/CSharpResources.resx | 4 +- .../Portable/xlf/CSharpResources.cs.xlf | 8 +-- .../Portable/xlf/CSharpResources.de.xlf | 8 +-- .../Portable/xlf/CSharpResources.es.xlf | 8 +-- .../Portable/xlf/CSharpResources.fr.xlf | 8 +-- .../Portable/xlf/CSharpResources.it.xlf | 8 +-- .../Portable/xlf/CSharpResources.ja.xlf | 8 +-- .../Portable/xlf/CSharpResources.ko.xlf | 8 +-- .../Portable/xlf/CSharpResources.pl.xlf | 8 +-- .../Portable/xlf/CSharpResources.pt-BR.xlf | 8 +-- .../Portable/xlf/CSharpResources.ru.xlf | 8 +-- .../Portable/xlf/CSharpResources.tr.xlf | 8 +-- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 8 +-- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 8 +-- .../Semantics/ExtensionOperatorsTests.cs | 24 ++++----- ...DefinedCompoundAssignmentOperatorsTests.cs | 2 +- 17 files changed, 99 insertions(+), 86 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs b/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs index 418c4cf3c3217..d054c4b6f92cb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs @@ -22,6 +22,13 @@ private struct OperatorResolutionForReporting private object? _nonExtensionResult; private object? _extensionResult; + [Conditional("DEBUG")] + private void AssertInvariant() + { + Debug.Assert(_nonExtensionResult is null or OverloadResolutionResult or BinaryOperatorOverloadResolutionResult or UnaryOperatorOverloadResolutionResult); + Debug.Assert(_extensionResult is null or OverloadResolutionResult or BinaryOperatorOverloadResolutionResult or UnaryOperatorOverloadResolutionResult); + } + /// Returns true if the result was set and took ownership of the result. private bool SaveResult(object result, ref object? savedResult) { @@ -71,7 +78,7 @@ public bool SaveResult(UnaryOperatorOverloadResolutionResult result, bool isExte /// /// Follows a very simplified version of OverloadResolutionResult.ReportDiagnostics which can be expanded in the future if needed. /// - internal bool TryReportDiagnostics(SyntaxNode node, BindingDiagnosticBag diagnostics, Binder binder, object leftDisplay, object? rightDisplay) + internal bool TryReportDiagnostics(SyntaxNode node, Binder binder, object leftDisplay, object? rightDisplay, BindingDiagnosticBag diagnostics) { object? resultToUse = pickResultToUse(_nonExtensionResult, _extensionResult); if (resultToUse is null) @@ -82,16 +89,22 @@ internal bool TryReportDiagnostics(SyntaxNode node, BindingDiagnosticBag diagnos var results = ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKind)>.GetInstance(); populateResults(results, resultToUse); - bool reported = tryReportDiagnostics(node, diagnostics, binder, results, leftDisplay, rightDisplay); + bool reported = tryReportDiagnostics(node, binder, results, leftDisplay, rightDisplay, diagnostics); results.Free(); return reported; - static bool tryReportDiagnostics(SyntaxNode node, BindingDiagnosticBag diagnostics, Binder binder, ArrayBuilder<(MethodSymbol? member, OperatorAnalysisResultKind resultKind)> results, object leftDisplay, object? rightDisplay) + static bool tryReportDiagnostics( + SyntaxNode node, + Binder binder, + ArrayBuilder<(MethodSymbol? member, OperatorAnalysisResultKind resultKind)> results, + object leftDisplay, + object? rightDisplay, + BindingDiagnosticBag diagnostics) { assertNone(results, OperatorAnalysisResultKind.Undefined); - if (hadAmbiguousBestMethods(results, node, diagnostics, binder)) + if (hadAmbiguousBestMethods(results, node, binder, diagnostics)) { return true; } @@ -102,6 +115,12 @@ static bool tryReportDiagnostics(SyntaxNode node, BindingDiagnosticBag diagnosti } assertNone(results, OperatorAnalysisResultKind.Applicable); + + if (results.Any(m => m.resultKind == OperatorAnalysisResultKind.Worse)) + { + return false; + } + assertNone(results, OperatorAnalysisResultKind.Worse); Debug.Assert(results.All(r => r.resultKind == OperatorAnalysisResultKind.Inapplicable)); @@ -112,12 +131,12 @@ static bool tryReportDiagnostics(SyntaxNode node, BindingDiagnosticBag diagnosti var toReport = nodeToReport(node); if (rightDisplay is null) { - // error: Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + // error: Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' Error(diagnostics, ErrorCode.ERR_SingleInapplicableUnaryOperator, toReport, leftDisplay, inapplicableMember); } else { - // error: Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + // error: Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' Error(diagnostics, ErrorCode.ERR_SingleInapplicableBinaryOperator, toReport, leftDisplay, rightDisplay, inapplicableMember); } @@ -165,7 +184,7 @@ static OperatorAnalysisResultKind getBestKind(object result) { if (res.Signature.Method is null) { - // Skip build-in operators + // Skip built-in operators continue; } @@ -181,7 +200,7 @@ static OperatorAnalysisResultKind getBestKind(object result) { if (res.Signature.Method is null) { - // Skip build-in operators + // Skip built-in operators continue; } @@ -199,7 +218,7 @@ static OperatorAnalysisResultKind getBestKind(object result) return bestKind; } - static bool hadAmbiguousBestMethods(ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKind)> results, SyntaxNode node, BindingDiagnosticBag diagnostics, Binder binder) + static bool hadAmbiguousBestMethods(ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKind)> results, SyntaxNode node, Binder binder, BindingDiagnosticBag diagnostics) { if (!tryGetTwoBest(results, out var first, out var second)) { @@ -222,6 +241,7 @@ static SyntaxNodeOrToken nodeToReport(SyntaxNode node) }; } + [Conditional("DEBUG")] static void assertNone(ArrayBuilder<(MethodSymbol? member, OperatorAnalysisResultKind resultKind)> results, OperatorAnalysisResultKind kind) { Debug.Assert(results.All(r => r.resultKind != kind)); @@ -259,9 +279,9 @@ static bool tryGetTwoBest(ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKin return false; } - static void populateResults(ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKind)> results, object? extensionResult) + static void populateResults(ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultKind)> results, object? result) { - switch (extensionResult) + switch (result) { case OverloadResolutionResult result1: foreach (var res in result1.ResultsBuilder) @@ -287,7 +307,7 @@ static void populateResults(ArrayBuilder<(MethodSymbol?, OperatorAnalysisResultK break; default: - throw ExceptionUtilities.UnexpectedValue(extensionResult); + throw ExceptionUtilities.UnexpectedValue(result); } } @@ -329,12 +349,5 @@ static void free(ref object? result) result = null; } } - - [Conditional("DEBUG")] - private void AssertInvariant() - { - Debug.Assert(_nonExtensionResult is null or OverloadResolutionResult or BinaryOperatorOverloadResolutionResult or UnaryOperatorOverloadResolutionResult); - Debug.Assert(_extensionResult is null or OverloadResolutionResult or BinaryOperatorOverloadResolutionResult or UnaryOperatorOverloadResolutionResult); - } } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 581a1e9f37a35..b4b9c076fbbd6 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -8172,10 +8172,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The extension resolution is ambiguous between the following members: '{0}' and '{1}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' Operator resolution is ambiguous between the following members: '{0}' and '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 046f20e466eea..0fee9eae6d5b6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 36dd240b4a333..6adc8fbaf373e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 635df60d0f4c0..88a84407eb3bd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 2b51b8f1cbe64..f39d4aea1e56a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 22fe251935e54..8429c78c0e20f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 1aa5ada1c3e6c..d7469555b0568 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index ffc2cf6ff4218..bd9d92da9e968 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 30957cb6a701b..ebc28c8f661ee 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 4c00dd6e968d4..ec7f0024ebbf8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index eccbc3d6599b7..343980a1a3c35 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 712d7fc9ed644..ea4d26823b5bb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index cbc98dff802d5..c2eebbb836bd2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 754f46b218488..395d221ce82b4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2393,13 +2393,13 @@ - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' + Operator cannot be applied to operands of type '{0}' and '{1}'. The closest inapplicable candidate is '{2}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' - Operator cannot be applied on operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' + Operator cannot be applied to operand of type '{0}'. The closest inapplicable candidate is '{1}' diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs index 34b469e6e31e9..fe6ae68030f70 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs @@ -6358,7 +6358,7 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (20,13): error CS9341: Operator cannot be applied on operand of type 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator ++()' + // (20,13): error CS9341: Operator cannot be applied to operand of type 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator ++()' // _ = ++s1; Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1?", "Extensions1.extension(ref S1).operator ++()").WithLocation(20, 13) ); @@ -6480,7 +6480,7 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (19,13): error CS9341: Operator cannot be applied on operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1?).operator ++()' + // (19,13): error CS9341: Operator cannot be applied to operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1?).operator ++()' // _ = ++s1; Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1", "Extensions1.extension(ref S1?).operator ++()").WithLocation(19, 13), // (20,44): error CS1620: Argument 1 must be passed with the 'ref' keyword @@ -6579,13 +6579,13 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (22,13): error CS9341: Operator cannot be applied on operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator ++()' + // (22,13): error CS9341: Operator cannot be applied to operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator ++()' // _ = ++s1; Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1", "Extensions1.extension(ref S2).operator ++()").WithLocation(22, 13), // (23,48): error CS1503: Argument 1: cannot convert from 'ref S1' to 'ref S2' // Extensions1.op_IncrementAssignment(ref s1); Diagnostic(ErrorCode.ERR_BadArgType, "s1").WithArguments("1", "ref S1", "ref S2").WithLocation(23, 48), - // (26,13): error CS9341: Operator cannot be applied on operand of type 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator ++()' + // (26,13): error CS9341: Operator cannot be applied to operand of type 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S2).operator ++()' // _ = ++s2; Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1?", "Extensions1.extension(ref S2).operator ++()").WithLocation(26, 13), // (27,48): error CS1503: Argument 1: cannot convert from 'ref S1?' to 'ref S2' @@ -6660,10 +6660,10 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,13): error CS9341: Operator cannot be applied on operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(object).operator ++()' + // (17,13): error CS9341: Operator cannot be applied to operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(object).operator ++()' // _ = ++s1; Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1", "Extensions1.extension(object).operator ++()").WithLocation(17, 13), - // (21,13): error CS9341: Operator cannot be applied on operand of type 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(object).operator ++()' + // (21,13): error CS9341: Operator cannot be applied to operand of type 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(object).operator ++()' // _ = ++s2; Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1?", "Extensions1.extension(object).operator ++()").WithLocation(21, 13) ); @@ -6866,7 +6866,7 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,13): error CS9341: Operator cannot be applied on operand of type 'int[]'. The closest inapplicable candidate is 'Extensions1.extension(ref Span).operator ++()' + // (17,13): error CS9341: Operator cannot be applied to operand of type 'int[]'. The closest inapplicable candidate is 'Extensions1.extension(ref Span).operator ++()' // _ = ++a1; Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("int[]", "Extensions1.extension(ref System.Span).operator ++()").WithLocation(17, 13), // (18,48): error CS1503: Argument 1: cannot convert from 'ref int[]' to 'ref System.Span' @@ -7207,7 +7207,7 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,13): error CS9341: Operator cannot be applied on operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator ++()' + // (17,13): error CS9341: Operator cannot be applied to operand of type 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator ++()' // _ = ++s1; Diagnostic(ErrorCode.ERR_SingleInapplicableUnaryOperator, "++").WithArguments("S1", "Extensions1.extension(ref S1).operator ++()").WithLocation(17, 13) ); @@ -19911,7 +19911,7 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (22,16): error CS9340: Operator cannot be applied on operands of type 'S2' and 'S2'. The closest inapplicable candidate is 'Extensions1.extension(S2).operator +(S2, int)' + // (22,16): error CS9340: Operator cannot be applied to operands of type 'S2' and 'S2'. The closest inapplicable candidate is 'Extensions1.extension(S2).operator +(S2, int)' // _ = s2 + s2; Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+").WithArguments("S2", "S2", "Extensions1.extension(S2).operator +(S2, int)").WithLocation(22, 16) ); @@ -21971,7 +21971,7 @@ static void Main() var comp = CreateCompilation(src); comp.VerifyDiagnostics( - // (18,17): error CS9340: Operator cannot be applied on operands of type 'S1?' and 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator %=(S1)' + // (18,17): error CS9340: Operator cannot be applied to operands of type 'S1?' and 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator %=(S1)' // _ = s11 %= s12; Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, op + "=").WithArguments("S1?", "S1?", "Extensions1.extension(ref S1).operator " + op + "=(S1)").WithLocation(18, 17) ); @@ -22091,10 +22091,10 @@ static void Main() var comp = CreateCompilation(src); comp.VerifyDiagnostics( - // (18,17): error CS9340: Operator cannot be applied on operands of type 'S1?' and 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator %=(S1)' + // (18,17): error CS9340: Operator cannot be applied to operands of type 'S1?' and 'S1'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator %=(S1)' // _ = s11 %= s12; Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, op + "=").WithArguments("S1?", "S1", "Extensions1.extension(ref S1).operator " + op + "=(S1)").WithLocation(18, 17), - // (19,17): error CS9340: Operator cannot be applied on operands of type 'S1' and 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator %=(S1)' + // (19,17): error CS9340: Operator cannot be applied to operands of type 'S1' and 'S1?'. The closest inapplicable candidate is 'Extensions1.extension(ref S1).operator %=(S1)' // _ = s12 %= s11; Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, op + "=").WithArguments("S1", "S1?", "Extensions1.extension(ref S1).operator " + op + "=(S1)").WithLocation(19, 17) ); diff --git a/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs b/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs index 6f46aa94fd1c9..720bad7a3cf30 100644 --- a/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Symbols/UserDefinedCompoundAssignmentOperatorsTests.cs @@ -16950,7 +16950,7 @@ static void Main() var comp2 = CreateCompilation([source2, CompilerFeatureRequiredAttribute], references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); comp2.VerifyDiagnostics( - // (7,11): error CS9340: Operator cannot be applied on operands of type 'C1' and 'int'. The closest inapplicable candidate is 'C1.operator +=(params int[])' + // (7,11): error CS9340: Operator cannot be applied to operands of type 'C1' and 'int'. The closest inapplicable candidate is 'C1.operator +=(params int[])' // x += 1; Diagnostic(ErrorCode.ERR_SingleInapplicableBinaryOperator, "+=").WithArguments("C1", "int", "C1.operator +=(params int[])").WithLocation(7, 11) ); From 9a868b727f417cbd0c65c80385f0e1359393e9bf Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 29 Oct 2025 17:42:17 -0700 Subject: [PATCH 3/6] Address feedback --- src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs | 6 ++---- .../CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index ef60e0faf67a5..f8bde81f1a9cc 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -1237,8 +1237,6 @@ bool bindSimpleBinaryOperatorPartsContinue( originalUserDefinedOperators = extensionOriginalUserDefinedOperators; foundOperator = bindSimpleBinaryOperatorPartsContinue(node, diagnostics, left, right, kind, ref resultKind, ref originalUserDefinedOperators, out resultSignature, ref best, isChecked, name1, name2Opt, ref operatorResolutionForReporting); } - - operatorResolutionForReporting.Free(); } } @@ -1299,7 +1297,7 @@ private void ReportUnaryOperatorError(CSharpSyntaxNode node, BindingDiagnosticBa return; } - if (operatorResolutionForReporting.TryReportDiagnostics(node, diagnostics, this, operand.Display, null)) + if (operatorResolutionForReporting.TryReportDiagnostics(node, this, operand.Display, null, diagnostics)) { return; } @@ -1334,7 +1332,7 @@ node.OperatorToken.RawKind is (int)SyntaxKind.PlusEqualsToken or (int)SyntaxKind private void ReportBinaryOperatorError(ExpressionSyntax node, BindingDiagnosticBag diagnostics, SyntaxToken operatorToken, BoundExpression left, BoundExpression right, LookupResultKind resultKind, ref OperatorResolutionForReporting operatorResolutionForReporting) { - if (operatorResolutionForReporting.TryReportDiagnostics(node, diagnostics, this, left.Display, right.Display)) + if (operatorResolutionForReporting.TryReportDiagnostics(node, this, left.Display, right.Display, diagnostics)) { return; } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs index fe6ae68030f70..88d2925d15198 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs @@ -30092,9 +30092,9 @@ public class C2 { } var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( - // (1,5): error CS0034: Operator '==' is ambiguous on operands of type 'C1' and 'C2' + // (1,14): error CS9342: Operator resolution is ambiguous between the following members: 'E1.extension(C1).operator ==(C1, C2)' and 'E2.extension(C1).operator ==(C1, C2)' // _ = new C1() == new C2(); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new C1() == new C2()").WithArguments("==", "C1", "C2").WithLocation(1, 5)); + Diagnostic(ErrorCode.ERR_AmbigOperator, "==").WithArguments("E1.extension(C1).operator ==(C1, C2)", "E2.extension(C1).operator ==(C1, C2)").WithLocation(1, 14)); } [Fact] From 87c9e6cd627434f9229cfb298049ac3f26520e2e Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 30 Oct 2025 14:41:26 -0700 Subject: [PATCH 4/6] Re-order parameter --- src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index f8bde81f1a9cc..ddfb68e3cb3fc 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -3364,8 +3364,8 @@ BoundExpression bindIncrementOperator(ExpressionSyntax node, ExpressionSyntax op staticOperatorName1, staticOperatorName2Opt, checkedInstanceOperatorName, ordinaryInstanceOperatorName, operand, diagnostics, - out staticExtensionBest, out staticExtensionResultKind, out staticExtensionOriginalUserDefinedOperators, - ref operatorResolutionForReporting); + ref operatorResolutionForReporting, out staticExtensionBest, out staticExtensionResultKind, + out staticExtensionOriginalUserDefinedOperators); if (instanceExtensionResult is not null) { @@ -3691,10 +3691,10 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol operandType, Instance string? ordinaryInstanceOperatorName, BoundExpression operand, BindingDiagnosticBag diagnostics, + ref OperatorResolutionForReporting operatorResolutionForReporting, out UnaryOperatorAnalysisResult? staticBest, out LookupResultKind staticResultKind, - out ImmutableArray staticOriginalUserDefinedOperators, - ref OperatorResolutionForReporting operatorResolutionForReporting) + out ImmutableArray staticOriginalUserDefinedOperators) { Debug.Assert(operand.Type is not null); From 99a248c8e47913246d8b56c2d7bc9eb211ec86cf Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 30 Oct 2025 14:46:07 -0700 Subject: [PATCH 5/6] Re-order parameters --- .../CSharp/Portable/Binder/Binder_Operators.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index ddfb68e3cb3fc..7c3ab1e9d1e0d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -176,8 +176,8 @@ BoundExpression bindCompoundAssignment(AssignmentExpressionSyntax node, BindingD staticOperatorName1, staticOperatorName2Opt, checkedInstanceOperatorName, ordinaryInstanceOperatorName, left, right, diagnostics, - out staticExtensionBest, out staticExtensionResultKind, out staticExtensionOriginalUserDefinedOperators, - ref operatorResolutionForReporting); + ref operatorResolutionForReporting, + out staticExtensionBest, out staticExtensionResultKind, out staticExtensionOriginalUserDefinedOperators); if (instanceExtensionResult is not null) { @@ -581,10 +581,10 @@ TypeSymbol getResultType(ExpressionSyntax node, TypeSymbol leftType, BindingDiag BoundExpression left, BoundExpression right, BindingDiagnosticBag diagnostics, + ref OperatorResolutionForReporting operatorResolutionForReporting, out BinaryOperatorAnalysisResult? staticBest, out LookupResultKind staticResultKind, - out ImmutableArray staticOriginalUserDefinedOperators, - ref OperatorResolutionForReporting operatorResolutionForReporting) + out ImmutableArray staticOriginalUserDefinedOperators) { staticBest = null; staticResultKind = LookupResultKind.Empty; @@ -3333,7 +3333,9 @@ BoundExpression bindIncrementOperator(ExpressionSyntax node, ExpressionSyntax op { Debug.Assert(ordinaryInstanceOperatorName is not null); - BoundIncrementOperator? inPlaceResult = tryApplyUserDefinedInstanceOperator(node, operatorToken, kind, mode, isChecked, checkedInstanceOperatorName, ordinaryInstanceOperatorName, operand, ref operatorResolutionForReporting, diagnostics); + BoundIncrementOperator? inPlaceResult = tryApplyUserDefinedInstanceOperator(node, operatorToken, kind, mode, isChecked, checkedInstanceOperatorName, ordinaryInstanceOperatorName, + operand, ref operatorResolutionForReporting, diagnostics); + if (inPlaceResult is not null) { return inPlaceResult; @@ -3364,8 +3366,8 @@ BoundExpression bindIncrementOperator(ExpressionSyntax node, ExpressionSyntax op staticOperatorName1, staticOperatorName2Opt, checkedInstanceOperatorName, ordinaryInstanceOperatorName, operand, diagnostics, - ref operatorResolutionForReporting, out staticExtensionBest, out staticExtensionResultKind, - out staticExtensionOriginalUserDefinedOperators); + ref operatorResolutionForReporting, + out staticExtensionBest, out staticExtensionResultKind, out staticExtensionOriginalUserDefinedOperators); if (instanceExtensionResult is not null) { From d3a09b875928e341f697ac877f6102fcf2e41be9 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 4 Nov 2025 00:57:37 -0800 Subject: [PATCH 6/6] Address feedback --- .../Binder.OperatorResolutionForReporting.cs | 6 +- .../Portable/Binder/Binder_Operators.cs | 68 +++++++++---------- .../Portable/Binder/Binder_Statements.cs | 2 +- .../Portable/Binder/Binder_TupleOperators.cs | 2 +- .../Semantics/ExtensionOperatorsTests.cs | 8 +-- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs b/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs index d054c4b6f92cb..e0f44bd5f9617 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.OperatorResolutionForReporting.cs @@ -23,7 +23,7 @@ private struct OperatorResolutionForReporting private object? _extensionResult; [Conditional("DEBUG")] - private void AssertInvariant() + private readonly void AssertInvariant() { Debug.Assert(_nonExtensionResult is null or OverloadResolutionResult or BinaryOperatorOverloadResolutionResult or UnaryOperatorOverloadResolutionResult); Debug.Assert(_extensionResult is null or OverloadResolutionResult or BinaryOperatorOverloadResolutionResult or UnaryOperatorOverloadResolutionResult); @@ -78,7 +78,7 @@ public bool SaveResult(UnaryOperatorOverloadResolutionResult result, bool isExte /// /// Follows a very simplified version of OverloadResolutionResult.ReportDiagnostics which can be expanded in the future if needed. /// - internal bool TryReportDiagnostics(SyntaxNode node, Binder binder, object leftDisplay, object? rightDisplay, BindingDiagnosticBag diagnostics) + internal readonly bool TryReportDiagnostics(SyntaxNode node, Binder binder, object leftDisplay, object? rightDisplay, BindingDiagnosticBag diagnostics) { object? resultToUse = pickResultToUse(_nonExtensionResult, _extensionResult); if (resultToUse is null) @@ -126,7 +126,7 @@ static bool tryReportDiagnostics( Debug.Assert(results.All(r => r.resultKind == OperatorAnalysisResultKind.Inapplicable)); // There is much room to improve diagnostics on inapplicable candidates, but for now we just report the candidate if there is a single one. - if (results.Count == 1 && results[0].member is { } inapplicableMember) + if (results is [{ member: { } inapplicableMember }]) { var toReport = nodeToReport(node); if (rightDisplay is null) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 7c3ab1e9d1e0d..3fd082fce17bf 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -24,11 +24,11 @@ internal partial class Binder private BoundExpression BindCompoundAssignment(AssignmentExpressionSyntax node, BindingDiagnosticBag diagnostics) { OperatorResolutionForReporting operatorResolutionForReporting = default; - BoundExpression result = bindCompoundAssignment(node, diagnostics, ref operatorResolutionForReporting); + BoundExpression result = bindCompoundAssignment(node, ref operatorResolutionForReporting, diagnostics); operatorResolutionForReporting.Free(); return result; - BoundExpression bindCompoundAssignment(AssignmentExpressionSyntax node, BindingDiagnosticBag diagnostics, ref OperatorResolutionForReporting operatorResolutionForReporting) + BoundExpression bindCompoundAssignment(AssignmentExpressionSyntax node, ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { node.Left.CheckDeconstructionCompatibleArgument(diagnostics); @@ -157,7 +157,7 @@ BoundExpression bindCompoundAssignment(AssignmentExpressionSyntax node, BindingD OverloadResolution.GetStaticUserDefinedBinaryOperatorMethodNames(kind, checkOverflowAtRuntime, out string staticOperatorName1, out string? staticOperatorName2Opt); BinaryOperatorAnalysisResult best = BinaryOperatorNonExtensionOverloadResolution(kind, isChecked: checkOverflowAtRuntime, staticOperatorName1, staticOperatorName2Opt, left, right, - node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); + node, diagnostics, ref operatorResolutionForReporting, out resultKind, out originalUserDefinedOperators); Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); Debug.Assert(best.HasValue == (resultKind is LookupResultKind.Viable)); @@ -1016,7 +1016,7 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Bi OperatorResolutionForReporting operatorResolutionForReporting = default; bool foundOperator = BindSimpleBinaryOperatorParts(node, diagnostics, left, right, kind, - out resultKind, out originalUserDefinedOperators, out signature, out best, ref operatorResolutionForReporting); + ref operatorResolutionForReporting, out resultKind, out originalUserDefinedOperators, out signature, out best); BinaryOperatorKind resultOperatorKind = signature.Kind; bool hasErrors = false; @@ -1136,8 +1136,8 @@ resultOperatorKind is BinaryOperatorKind.ObjectEqual or BinaryOperatorKind.Objec } private bool BindSimpleBinaryOperatorParts(BinaryExpressionSyntax node, BindingDiagnosticBag diagnostics, BoundExpression left, BoundExpression right, BinaryOperatorKind kind, - out LookupResultKind resultKind, out ImmutableArray originalUserDefinedOperators, - out BinaryOperatorSignature resultSignature, out BinaryOperatorAnalysisResult best, ref OperatorResolutionForReporting operatorResolutionForReporting) + ref OperatorResolutionForReporting operatorResolutionForReporting, out LookupResultKind resultKind, + out ImmutableArray originalUserDefinedOperators, out BinaryOperatorSignature resultSignature, out BinaryOperatorAnalysisResult best) { if (!IsTypelessExpressionAllowedInBinaryOperator(kind, left, right)) { @@ -1150,7 +1150,7 @@ private bool BindSimpleBinaryOperatorParts(BinaryExpressionSyntax node, BindingD bool isChecked = CheckOverflowAtRuntime; OverloadResolution.GetStaticUserDefinedBinaryOperatorMethodNames(kind, isChecked, out string name1, out string name2Opt); - best = this.BinaryOperatorOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); + best = this.BinaryOperatorOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, ref operatorResolutionForReporting, out resultKind, out originalUserDefinedOperators); return bindSimpleBinaryOperatorPartsContinue(node, diagnostics, left, right, kind, ref resultKind, ref originalUserDefinedOperators, out resultSignature, ref best, isChecked, name1, name2Opt, ref operatorResolutionForReporting); @@ -1228,7 +1228,7 @@ bool bindSimpleBinaryOperatorPartsContinue( LookupResultKind extensionResultKind; ImmutableArray extensionOriginalUserDefinedOperators; BinaryOperatorAnalysisResult? extensionBest = BinaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, - out extensionResultKind, out extensionOriginalUserDefinedOperators, ref operatorResolutionForReporting); + ref operatorResolutionForReporting, out extensionResultKind, out extensionOriginalUserDefinedOperators); if (extensionBest.HasValue) { @@ -1432,11 +1432,11 @@ private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax no private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax node, BoundExpression left, BoundExpression right, BindingDiagnosticBag diagnostics) { OperatorResolutionForReporting operatorResolutionForReporting = default; - BoundExpression result = bindConditionalLogicalOperator(node, left, right, diagnostics, ref operatorResolutionForReporting); + BoundExpression result = bindConditionalLogicalOperator(node, left, right, ref operatorResolutionForReporting, diagnostics); operatorResolutionForReporting.Free(); return result; - BoundExpression bindConditionalLogicalOperator(BinaryExpressionSyntax node, BoundExpression left, BoundExpression right, BindingDiagnosticBag diagnostics, ref OperatorResolutionForReporting operatorResolutionForReporting) + BoundExpression bindConditionalLogicalOperator(BinaryExpressionSyntax node, BoundExpression left, BoundExpression right, ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { BinaryOperatorKind kind = SyntaxKindToBinaryOperatorKind(node.Kind()); @@ -1485,7 +1485,7 @@ BoundExpression bindConditionalLogicalOperator(BinaryExpressionSyntax node, Boun } else { - best = this.BinaryOperatorOverloadResolution(kind, isChecked: CheckOverflowAtRuntime, left, right, node, diagnostics, out lookupResult, out originalUserDefinedOperators, ref operatorResolutionForReporting); + best = this.BinaryOperatorOverloadResolution(kind, isChecked: CheckOverflowAtRuntime, left, right, node, diagnostics, ref operatorResolutionForReporting, out lookupResult, out originalUserDefinedOperators); } // SPEC: If overload resolution fails to find a single best operator, or if overload @@ -2065,13 +2065,13 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( BoundExpression right, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, + ref OperatorResolutionForReporting operatorResolutionForReporting, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators, - ref OperatorResolutionForReporting operatorResolutionForReporting) + out ImmutableArray originalUserDefinedOperators) { OverloadResolution.GetStaticUserDefinedBinaryOperatorMethodNames(kind, isChecked, out string name1, out string name2Opt); - return BinaryOperatorOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); + return BinaryOperatorOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, ref operatorResolutionForReporting, out resultKind, out originalUserDefinedOperators); } private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( @@ -2083,11 +2083,11 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( BoundExpression right, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, + ref OperatorResolutionForReporting operatorResolutionForReporting, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators, - ref OperatorResolutionForReporting operatorResolutionForReporting) + out ImmutableArray originalUserDefinedOperators) { - BinaryOperatorAnalysisResult possiblyBest = BinaryOperatorNonExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); + BinaryOperatorAnalysisResult possiblyBest = BinaryOperatorNonExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, ref operatorResolutionForReporting, out resultKind, out originalUserDefinedOperators); Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); Debug.Assert(possiblyBest.HasValue == (resultKind is LookupResultKind.Viable)); @@ -2100,7 +2100,7 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( LookupResultKind extensionResultKind; ImmutableArray extensionOriginalUserDefinedOperators; - BinaryOperatorAnalysisResult? extensionBest = BinaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, out extensionResultKind, out extensionOriginalUserDefinedOperators, ref operatorResolutionForReporting); + BinaryOperatorAnalysisResult? extensionBest = BinaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, left, right, node, diagnostics, ref operatorResolutionForReporting, out extensionResultKind, out extensionOriginalUserDefinedOperators); if (extensionBest.HasValue) { @@ -2124,9 +2124,9 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution( BoundExpression right, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, + ref OperatorResolutionForReporting operatorResolutionForReporting, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators, - ref OperatorResolutionForReporting operatorResolutionForReporting) + out ImmutableArray originalUserDefinedOperators) { resultKind = LookupResultKind.Empty; originalUserDefinedOperators = []; @@ -2184,9 +2184,9 @@ private BinaryOperatorAnalysisResult BinaryOperatorNonExtensionOverloadResolutio BoundExpression right, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, + ref OperatorResolutionForReporting operatorResolutionForReporting, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators, - ref OperatorResolutionForReporting operatorResolutionForReporting) + out ImmutableArray originalUserDefinedOperators) { var result = BinaryOperatorOverloadResolutionResult.GetInstance(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); @@ -2286,14 +2286,14 @@ private UnaryOperatorAnalysisResult UnaryOperatorOverloadResolution( BoundExpression operand, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, + ref OperatorResolutionForReporting operatorResolutionForReporting, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators, - ref OperatorResolutionForReporting operatorResolutionForReporting) + out ImmutableArray originalUserDefinedOperators) { bool isChecked = CheckOverflowAtRuntime; OverloadResolution.GetStaticUserDefinedUnaryOperatorMethodNames(kind, isChecked, out string name1, out string name2Opt); - var best = UnaryOperatorNonExtensionOverloadResolution(kind, isChecked, name1, name2Opt, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); + var best = UnaryOperatorNonExtensionOverloadResolution(kind, isChecked, name1, name2Opt, operand, node, diagnostics, ref operatorResolutionForReporting, out resultKind, out originalUserDefinedOperators); Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); Debug.Assert(best.HasValue == (resultKind is LookupResultKind.Viable)); @@ -2307,7 +2307,7 @@ private UnaryOperatorAnalysisResult UnaryOperatorOverloadResolution( LookupResultKind extensionResultKind; ImmutableArray extensionOriginalUserDefinedOperators; UnaryOperatorAnalysisResult? extensionBest = this.UnaryOperatorExtensionOverloadResolution(kind, isChecked, name1, name2Opt, operand, node, diagnostics, - out extensionResultKind, out extensionOriginalUserDefinedOperators, ref operatorResolutionForReporting); + ref operatorResolutionForReporting, out extensionResultKind, out extensionOriginalUserDefinedOperators); if (extensionBest.HasValue) { @@ -2328,9 +2328,9 @@ private UnaryOperatorAnalysisResult UnaryOperatorNonExtensionOverloadResolution( BoundExpression operand, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, + ref OperatorResolutionForReporting operatorResolutionForReporting, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators, - ref OperatorResolutionForReporting operatorResolutionForReporting) + out ImmutableArray originalUserDefinedOperators) { var result = UnaryOperatorOverloadResolutionResult.GetInstance(); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); @@ -2435,9 +2435,9 @@ static bool isNuint(TypeSymbol type) BoundExpression operand, CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, + ref OperatorResolutionForReporting operatorResolutionForReporting, out LookupResultKind resultKind, - out ImmutableArray originalUserDefinedOperators, - ref OperatorResolutionForReporting operatorResolutionForReporting) + out ImmutableArray originalUserDefinedOperators) { resultKind = LookupResultKind.Empty; originalUserDefinedOperators = []; @@ -3272,11 +3272,11 @@ private enum InstanceUserDefinedIncrementUsageMode : byte private BoundExpression BindIncrementOperator(ExpressionSyntax node, ExpressionSyntax operandSyntax, SyntaxToken operatorToken, BindingDiagnosticBag diagnostics) { OperatorResolutionForReporting operatorResolutionForReporting = default; - BoundExpression result = bindIncrementOperator(node, operandSyntax, operatorToken, diagnostics, ref operatorResolutionForReporting); + BoundExpression result = bindIncrementOperator(node, operandSyntax, operatorToken, ref operatorResolutionForReporting, diagnostics); operatorResolutionForReporting.Free(); return result; - BoundExpression bindIncrementOperator(ExpressionSyntax node, ExpressionSyntax operandSyntax, SyntaxToken operatorToken, BindingDiagnosticBag diagnostics, ref OperatorResolutionForReporting operatorResolutionForReporting) + BoundExpression bindIncrementOperator(ExpressionSyntax node, ExpressionSyntax operandSyntax, SyntaxToken operatorToken, ref OperatorResolutionForReporting operatorResolutionForReporting, BindingDiagnosticBag diagnostics) { operandSyntax.CheckDeconstructionCompatibleArgument(diagnostics); @@ -3346,7 +3346,7 @@ BoundExpression bindIncrementOperator(ExpressionSyntax node, ExpressionSyntax op LookupResultKind resultKind; ImmutableArray originalUserDefinedOperators; - var best = this.UnaryOperatorNonExtensionOverloadResolution(kind, isChecked, staticOperatorName1, staticOperatorName2Opt, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); + var best = this.UnaryOperatorNonExtensionOverloadResolution(kind, isChecked, staticOperatorName1, staticOperatorName2Opt, operand, node, diagnostics, ref operatorResolutionForReporting, out resultKind, out originalUserDefinedOperators); Debug.Assert(resultKind is LookupResultKind.Viable or LookupResultKind.Ambiguous or LookupResultKind.OverloadResolutionFailure or LookupResultKind.Empty); Debug.Assert(best.HasValue == (resultKind is LookupResultKind.Viable)); @@ -4390,7 +4390,7 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper ImmutableArray originalUserDefinedOperators; OperatorResolutionForReporting operatorResolutionForReporting = default; - var best = this.UnaryOperatorOverloadResolution(kind, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref operatorResolutionForReporting); + var best = this.UnaryOperatorOverloadResolution(kind, operand, node, diagnostics, ref operatorResolutionForReporting, out resultKind, out originalUserDefinedOperators); if (!best.HasValue) { ReportUnaryOperatorError(node, diagnostics, operatorText, operand, resultKind, ref operatorResolutionForReporting); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index daf9fd64ddd7f..c5ab6cdd98d20 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -2743,7 +2743,7 @@ internal BoundExpression BindBooleanExpression(ExpressionSyntax node, BindingDia // It was not. Does it implement operator true? expr = BindToNaturalType(expr, diagnostics); OperatorResolutionForReporting discardedOperatorResolutionForReporting = default; - var best = this.UnaryOperatorOverloadResolution(UnaryOperatorKind.True, expr, node, diagnostics, out LookupResultKind resultKind, out ImmutableArray originalUserDefinedOperators, ref discardedOperatorResolutionForReporting); + var best = this.UnaryOperatorOverloadResolution(UnaryOperatorKind.True, expr, node, diagnostics, ref discardedOperatorResolutionForReporting, out LookupResultKind resultKind, out ImmutableArray originalUserDefinedOperators); discardedOperatorResolutionForReporting.Free(); if (!best.HasValue) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs index 7bc941119f54a..e59aaaabfdc01 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs @@ -159,7 +159,7 @@ private void PrepareBoolConversionAndTruthOperator(TypeSymbol type, BinaryExpres ImmutableArray originalUserDefinedOperators; BoundExpression comparisonResult = new BoundTupleOperandPlaceholder(node, type); OperatorResolutionForReporting discardedOperatorResolutionForReporting = default; - UnaryOperatorAnalysisResult best = this.UnaryOperatorOverloadResolution(boolOpKind, comparisonResult, node, diagnostics, out resultKind, out originalUserDefinedOperators, ref discardedOperatorResolutionForReporting); + UnaryOperatorAnalysisResult best = this.UnaryOperatorOverloadResolution(boolOpKind, comparisonResult, node, diagnostics, ref discardedOperatorResolutionForReporting, out resultKind, out originalUserDefinedOperators); discardedOperatorResolutionForReporting.Free(); if (best.HasValue) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs index 88d2925d15198..70c4d2b5196b8 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionOperatorsTests.cs @@ -958,7 +958,7 @@ static void Main() var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); comp.VerifyEmitDiagnostics( - // (32,17): error CS9339: Operator resolution is ambiguous between the following members:'I1.operator -(I1)' and 'I3.operator -(I3)' + // (32,17): error CS9342: Operator resolution is ambiguous between the following members: 'I1.operator -(I1)' and 'I3.operator -(I3)' // var y = -x; Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("I1.operator -(I1)", "I3.operator -(I3)").WithLocation(32, 17) ); @@ -1733,10 +1733,10 @@ static void Main() var comp = CreateCompilation(src, options: TestOptions.DebugExe); #if DEBUG comp.VerifyEmitDiagnostics( - // (35,13): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions2.extension(C1).operator -(C1)' and 'Extensions1.extension(C1).operator -(C1)' + // (35,13): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions2.extension(C1).operator -(C1)' and 'Extensions1.extension(C1).operator -(C1)' // _ = -c1; Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions2.extension(C1).operator -(C1)", "Extensions1.extension(C1).operator -(C1)").WithLocation(35, 13), - // (39,17): error CS9339: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator checked -(C1)' and 'Extensions2.extension(C1).operator -(C1)' + // (39,17): error CS9342: Operator resolution is ambiguous between the following members: 'Extensions1.extension(C1).operator checked -(C1)' and 'Extensions2.extension(C1).operator -(C1)' // _ = -c1; Diagnostic(ErrorCode.ERR_AmbigOperator, "-").WithArguments("Extensions1.extension(C1).operator checked -(C1)", "Extensions2.extension(C1).operator -(C1)").WithLocation(39, 17) ); @@ -29470,7 +29470,7 @@ .method public hidebysig static void op_IncrementAssignment ( valuetype S s ) ci [Fact] public void ReportDiagnostics_CompoundAssignment_01() { - // inner scope has inapplicable operator, outter scope too + // inner scope has inapplicable operator, outer scope too var src = """ using N;