ICSharpCode.NRefactory.CSharp.Resolver.CSharpResolver.ResolveBinaryOperator C# (CSharp) Method

ResolveBinaryOperator() public method

public ResolveBinaryOperator ( BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs ) : ResolveResult
op BinaryOperatorType
lhs ResolveResult
rhs ResolveResult
return ResolveResult
		public ResolveResult ResolveBinaryOperator(BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs)
		{
			if (lhs.Type.Kind == TypeKind.Dynamic || rhs.Type.Kind == TypeKind.Dynamic) {
				lhs = Convert(lhs, SpecialType.Dynamic);
				rhs = Convert(rhs, SpecialType.Dynamic);
				return BinaryOperatorResolveResult(SpecialType.Dynamic, lhs, op, rhs);
			}
			
			// C# 4.0 spec: ยง7.3.4 Binary operator overload resolution
			string overloadableOperatorName = GetOverloadableOperatorName(op);
			if (overloadableOperatorName == null) {
				
				// Handle logical and/or exactly as bitwise and/or:
				// - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator.
				// - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit.
				// - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit
				if (op == BinaryOperatorType.ConditionalAnd) {
					overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseAnd);
				} else if (op == BinaryOperatorType.ConditionalOr) {
					overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseOr);
				} else if (op == BinaryOperatorType.NullCoalescing) {
					// null coalescing operator is not overloadable and needs to be handled separately
					return ResolveNullCoalescingOperator(lhs, rhs);
				} else {
					return ErrorResolveResult.UnknownError;
				}
			}
			
			// If the type is nullable, get the underlying type:
			bool isNullable = NullableType.IsNullable(lhs.Type) || NullableType.IsNullable(rhs.Type);
			IType lhsType = NullableType.GetUnderlyingType(lhs.Type);
			IType rhsType = NullableType.GetUnderlyingType(rhs.Type);
			
			// the operator is overloadable:
			OverloadResolution userDefinedOperatorOR = CreateOverloadResolution(new[] { lhs, rhs });
			HashSet<IParameterizedMember> userOperatorCandidates = new HashSet<IParameterizedMember>();
			userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName));
			userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName));
			foreach (var candidate in userOperatorCandidates) {
				userDefinedOperatorOR.AddCandidate(candidate);
			}
			if (userDefinedOperatorOR.FoundApplicableCandidate) {
				return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
			}
			
			if (lhsType.Kind == TypeKind.Null && rhsType.IsReferenceType == false
			    || lhsType.IsReferenceType == false && rhsType.Kind == TypeKind.Null)
			{
				isNullable = true;
			}
			if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight) {
				// special case: the shift operators allow "var x = null << null", producing int?.
				if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
					isNullable = true;
				// for shift operators, do unary promotion independently on both arguments
				lhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref lhsType, isNullable, lhs);
				rhs = UnaryNumericPromotion(UnaryOperatorType.Plus, ref rhsType, isNullable, rhs);
			} else {
				bool allowNullableConstants = op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality;
				if (!BinaryNumericPromotion(isNullable, ref lhs, ref rhs, allowNullableConstants))
					return new ErrorResolveResult(lhs.Type);
			}
			// re-read underlying types after numeric promotion
			lhsType = NullableType.GetUnderlyingType(lhs.Type);
			rhsType = NullableType.GetUnderlyingType(rhs.Type);
			
			IEnumerable<CSharpOperators.OperatorMethod> methodGroup;
			CSharpOperators operators = CSharpOperators.Get(compilation);
			switch (op) {
				case BinaryOperatorType.Multiply:
					methodGroup = operators.MultiplicationOperators;
					break;
				case BinaryOperatorType.Divide:
					methodGroup = operators.DivisionOperators;
					break;
				case BinaryOperatorType.Modulus:
					methodGroup = operators.RemainderOperators;
					break;
				case BinaryOperatorType.Add:
					methodGroup = operators.AdditionOperators;
					{
						if (lhsType.Kind == TypeKind.Enum) {
							// E operator +(E x, U y);
							IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable);
							if (TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs)) {
								return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
							}
						}
						if (rhsType.Kind == TypeKind.Enum) {
							// E operator +(U x, E y);
							IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable);
							if (TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs)) {
								return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
							}
						}
						
						if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) {
							return BinaryOperatorResolveResult(lhsType, lhs, op, rhs);
						} else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) {
							return BinaryOperatorResolveResult(rhsType, lhs, op, rhs);
						}
						
						if (lhsType is PointerType) {
							methodGroup = new [] {
								PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32),
								PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32),
								PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64),
								PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64)
							};
						} else if (rhsType is PointerType) {
							methodGroup = new [] {
								PointerArithmeticOperator(rhsType, KnownTypeCode.Int32, rhsType),
								PointerArithmeticOperator(rhsType, KnownTypeCode.UInt32, rhsType),
								PointerArithmeticOperator(rhsType, KnownTypeCode.Int64, rhsType),
								PointerArithmeticOperator(rhsType, KnownTypeCode.UInt64, rhsType)
							};
						}
						if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
							return new ErrorResolveResult(SpecialType.NullType);
					}
					break;
				case BinaryOperatorType.Subtract:
					methodGroup = operators.SubtractionOperators;
					{
						if (lhsType.Kind == TypeKind.Enum) {
							// U operator โ€“(E x, E y);
							if (TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs, allowConversionFromConstantZero: false)) {
								return HandleEnumSubtraction(isNullable, lhsType, lhs, rhs);
							}

							// E operator โ€“(E x, U y);
							IType underlyingType = MakeNullable(GetEnumUnderlyingType(lhsType), isNullable);
							if (TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs)) {
								return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
							}
						}
						if (rhsType.Kind == TypeKind.Enum) {
							// U operator โ€“(E x, E y);
							if (TryConvertEnum(ref lhs, rhs.Type, ref isNullable, ref rhs)) {
								return HandleEnumSubtraction(isNullable, rhsType, lhs, rhs);
							}

							// E operator -(U x, E y);
							IType underlyingType = MakeNullable(GetEnumUnderlyingType(rhsType), isNullable);
							if (TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs)) {
								return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
							}
						}
						
						if (lhsType.Kind == TypeKind.Delegate && TryConvert(ref rhs, lhsType)) {
							return BinaryOperatorResolveResult(lhsType, lhs, op, rhs);
						} else if (rhsType.Kind == TypeKind.Delegate && TryConvert(ref lhs, rhsType)) {
							return BinaryOperatorResolveResult(rhsType, lhs, op, rhs);
						}
						
						if (lhsType is PointerType) {
							if (rhsType is PointerType) {
								IType int64 = compilation.FindType(KnownTypeCode.Int64);
								if (lhsType.Equals(rhsType)) {
									return BinaryOperatorResolveResult(int64, lhs, op, rhs);
								} else {
									return new ErrorResolveResult(int64);
								}
							}
							methodGroup = new [] {
								PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int32),
								PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt32),
								PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.Int64),
								PointerArithmeticOperator(lhsType, lhsType, KnownTypeCode.UInt64)
							};
						}
						
						if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
							return new ErrorResolveResult(SpecialType.NullType);
					}
					break;
				case BinaryOperatorType.ShiftLeft:
					methodGroup = operators.ShiftLeftOperators;
					break;
				case BinaryOperatorType.ShiftRight:
					methodGroup = operators.ShiftRightOperators;
					break;
				case BinaryOperatorType.Equality:
				case BinaryOperatorType.InEquality:
				case BinaryOperatorType.LessThan:
				case BinaryOperatorType.GreaterThan:
				case BinaryOperatorType.LessThanOrEqual:
				case BinaryOperatorType.GreaterThanOrEqual:
					{
						if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) {
							// bool operator op(E x, E y);
							return HandleEnumComparison(op, lhsType, isNullable, lhs, rhs);
						} else if (rhsType.Kind == TypeKind.Enum && TryConvert(ref lhs, rhs.Type)) {
							// bool operator op(E x, E y);
							return HandleEnumComparison(op, rhsType, isNullable, lhs, rhs);
						} else if (lhsType is PointerType && rhsType is PointerType) {
							return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs);
						}
						if (op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality) {
							if (lhsType.IsReferenceType == true && rhsType.IsReferenceType == true) {
								// If it's a reference comparison
								if (op == BinaryOperatorType.Equality)
									methodGroup = operators.ReferenceEqualityOperators;
								else
									methodGroup = operators.ReferenceInequalityOperators;
								break;
							} else if (lhsType.Kind == TypeKind.Null && IsNullableTypeOrNonValueType(rhs.Type)
							           || IsNullableTypeOrNonValueType(lhs.Type) && rhsType.Kind == TypeKind.Null) {
								// compare type parameter or nullable type with the null literal
								return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs);
							}
						}
						switch (op) {
							case BinaryOperatorType.Equality:
								methodGroup = operators.ValueEqualityOperators;
								break;
							case BinaryOperatorType.InEquality:
								methodGroup = operators.ValueInequalityOperators;
								break;
							case BinaryOperatorType.LessThan:
								methodGroup = operators.LessThanOperators;
								break;
							case BinaryOperatorType.GreaterThan:
								methodGroup = operators.GreaterThanOperators;
								break;
							case BinaryOperatorType.LessThanOrEqual:
								methodGroup = operators.LessThanOrEqualOperators;
								break;
							case BinaryOperatorType.GreaterThanOrEqual:
								methodGroup = operators.GreaterThanOrEqualOperators;
								break;
							default:
								throw new InvalidOperationException();
						}
					}
					break;
				case BinaryOperatorType.BitwiseAnd:
				case BinaryOperatorType.BitwiseOr:
				case BinaryOperatorType.ExclusiveOr:
					{
						if (lhsType.Kind == TypeKind.Enum) {
							// bool operator op(E x, E y);
							if (TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs)) {
								return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
							}
						}

						if (rhsType.Kind == TypeKind.Enum) {
							// bool operator op(E x, E y);
							if (TryConvertEnum (ref lhs, rhs.Type, ref isNullable, ref rhs)) {
								return HandleEnumOperator(isNullable, rhsType, op, lhs, rhs);
							}
						}
						
						switch (op) {
							case BinaryOperatorType.BitwiseAnd:
								methodGroup = operators.BitwiseAndOperators;
								break;
							case BinaryOperatorType.BitwiseOr:
								methodGroup = operators.BitwiseOrOperators;
								break;
							case BinaryOperatorType.ExclusiveOr:
								methodGroup = operators.BitwiseXorOperators;
								break;
							default:
								throw new InvalidOperationException();
						}
					}
					break;
				case BinaryOperatorType.ConditionalAnd:
					methodGroup = operators.LogicalAndOperators;
					break;
				case BinaryOperatorType.ConditionalOr:
					methodGroup = operators.LogicalOrOperators;
					break;
				default:
					throw new InvalidOperationException();
			}
			OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { lhs, rhs });
			foreach (var candidate in methodGroup) {
				builtinOperatorOR.AddCandidate(candidate);
			}
			CSharpOperators.BinaryOperatorMethod m = (CSharpOperators.BinaryOperatorMethod)builtinOperatorOR.BestCandidate;
			IType resultType = m.ReturnType;
			if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None) {
				// If there are any user-defined operators, prefer those over the built-in operators.
				// It'll be a more informative error.
				if (userDefinedOperatorOR.BestCandidate != null)
					return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
				else
					return new ErrorResolveResult(resultType);
			} else if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) {
				object val;
				try {
					val = m.Invoke(this, lhs.ConstantValue, rhs.ConstantValue);
				} catch (ArithmeticException) {
					return new ErrorResolveResult(resultType);
				}
				return new ConstantResolveResult(resultType, val);
			} else {
				lhs = Convert(lhs, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
				rhs = Convert(rhs, m.Parameters[1].Type, builtinOperatorOR.ArgumentConversions[1]);
				return BinaryOperatorResolveResult(resultType, lhs, op, rhs,
				                                   builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator);
			}
		}
		

Usage Example

        public void EnumBitwiseOrWithMissingBaseType()
        {
            var resolver = new CSharpResolver(enumWithMissingBaseType.Compilation);
            var lhs      = new ConstantResolveResult(enumWithMissingBaseType, 1);
            var rhs      = new ConstantResolveResult(enumWithMissingBaseType, 2);
            var rr       = (ConstantResolveResult)resolver.ResolveBinaryOperator(BinaryOperatorType.BitwiseOr, lhs, rhs);

            Assert.AreEqual(enumWithMissingBaseType, rr.Type);
            Assert.AreEqual(3, rr.ConstantValue);
        }
All Usage Examples Of ICSharpCode.NRefactory.CSharp.Resolver.CSharpResolver::ResolveBinaryOperator
CSharpResolver