| File: Recommendations\CSharpRecommendationServiceRunner_Operators.cs | Web Access |
| Project: src\roslyn\src\Workspaces\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Workspaces.csproj (Microsoft.CodeAnalysis.CSharp.Workspaces) |
// 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.Linq; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Recommendations; internal partial class CSharpRecommendationService { /// <summary> /// Adds user defined operators to the unnamed recommendation set. /// </summary> private sealed partial class CSharpRecommendationServiceRunner { private static void AddOperators(ITypeSymbol container, ArrayBuilder<ISymbol> symbols) { var containerWithoutNullable = container.RemoveNullableIfPresent(); // Don't bother showing operators for basic built-in types. They're well known already and will only // clutter the display. if (ExcludeOperatorType(containerWithoutNullable)) return; var containerIsNullable = container.IsNullable(); foreach (var type in containerWithoutNullable.GetBaseTypesAndThis()) { foreach (var member in type.GetMembers()) { if (member is not IMethodSymbol { MethodKind: MethodKind.UserDefinedOperator } method) continue; // Don't add operator true/false. They only are used for conversions in special boolean contexts // (like `if` statement conditions), and are not invoked explicitly by the user. if (method.Name is WellKnownMemberNames.TrueOperatorName or WellKnownMemberNames.FalseOperatorName) continue; // If we're on a nullable version of the type, but this operator wouldn't naturally 'lift' to be // available for it, then don't include it. if (containerIsNullable && !IsLiftableOperator(method)) continue; // We don't need to bother lifting operators. We'll just show the basic operator in the list as the // information for it is sufficient for completion (i.e. we only insert the operator itself, not any // of the parameter or return types). symbols.Add(method); } } } private static bool ExcludeOperatorType(ITypeSymbol container) => container.IsSpecialType() || container.SpecialType is SpecialType.System_IntPtr or SpecialType.System_UIntPtr; private static bool IsLiftableOperator(IMethodSymbol symbol) { // https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#lifted-operators // Common for all: if (symbol.IsUserDefinedOperator() && symbol.Parameters.All(p => p.Type.IsValueType)) { switch (symbol.Name) { // Unary case WellKnownMemberNames.UnaryPlusOperatorName: case WellKnownMemberNames.IncrementOperatorName: case WellKnownMemberNames.UnaryNegationOperatorName: case WellKnownMemberNames.DecrementOperatorName: case WellKnownMemberNames.LogicalNotOperatorName: case WellKnownMemberNames.OnesComplementOperatorName: return symbol.Parameters.Length == 1 && symbol.ReturnType.IsValueType; // Binary case WellKnownMemberNames.AdditionOperatorName: case WellKnownMemberNames.SubtractionOperatorName: case WellKnownMemberNames.MultiplyOperatorName: case WellKnownMemberNames.DivisionOperatorName: case WellKnownMemberNames.ModulusOperatorName: case WellKnownMemberNames.BitwiseAndOperatorName: case WellKnownMemberNames.BitwiseOrOperatorName: case WellKnownMemberNames.ExclusiveOrOperatorName: case WellKnownMemberNames.LeftShiftOperatorName: case WellKnownMemberNames.RightShiftOperatorName: case WellKnownMemberNames.UnsignedRightShiftOperatorName: return symbol.Parameters.Length == 2 && symbol.ReturnType.IsValueType; // Equality + Relational case WellKnownMemberNames.EqualityOperatorName: case WellKnownMemberNames.InequalityOperatorName: case WellKnownMemberNames.LessThanOperatorName: case WellKnownMemberNames.GreaterThanOperatorName: case WellKnownMemberNames.LessThanOrEqualOperatorName: case WellKnownMemberNames.GreaterThanOrEqualOperatorName: return symbol.Parameters.Length == 2 && symbol.ReturnType.SpecialType == SpecialType.System_Boolean; } } return false; } } }