diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index b524045a11..788ea2445a 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -2095,6 +2095,25 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #endregion #region ResolveConditional + /// + /// Converts the input to bool using the rules for boolean expressions. + /// That is, operator true is used if a regular conversion to bool is not possible. + /// + public ResolveResult ResolveCondition(ResolveResult input) + { + if (input == null) + throw new ArgumentNullException("input"); + IType boolean = compilation.FindType(KnownTypeCode.Boolean); + Conversion c = conversions.ImplicitConversion(input, boolean); + if (!c.IsValid) { + var opTrue = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_True").FirstOrDefault(); + if (opTrue != null) { + c = Conversion.UserDefinedImplicitConversion(opTrue, false); + } + } + return Convert(input, boolean, c); + } + public ResolveResult ResolveConditional(ResolveResult condition, ResolveResult trueExpression, ResolveResult falseExpression) { // C# 4.0 spec ยง7.14: Conditional operator @@ -2131,7 +2150,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } else { return ErrorResult; } - isValid &= TryConvert(ref condition, compilation.FindType(KnownTypeCode.Boolean)); + condition = ResolveCondition(condition); if (isValid) { if (condition.IsCompileTimeConstant && trueExpression.IsCompileTimeConstant && falseExpression.IsCompileTimeConstant) { bool? val = condition.ConstantValue as bool?; diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs index a8802127bc..464b5afd46 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs @@ -2604,7 +2604,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (resolverEnabled) { for (AstNode child = conditionStatement.FirstChild; child != null; child = child.NextSibling) { if (child.Role == AstNode.Roles.Condition) { - ResolveAndProcessConversion((Expression)child, resolver.Compilation.FindType(KnownTypeCode.Boolean)); + Expression condition = (Expression)child; + ResolveResult conditionRR = Resolve(condition); + ResolveResult convertedRR = resolver.ResolveCondition(conditionRR); + if (convertedRR != conditionRR) + ProcessConversionResult(condition, convertedRR as ConversionResolveResult); } else { Scan(child); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/OperatorDeclarationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/OperatorDeclarationTests.cs index 034b525490..cf8afda519 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/OperatorDeclarationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/OperatorDeclarationTests.cs @@ -64,5 +64,11 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers Assert.AreEqual("MyObject", ((SimpleType)od.ReturnType).Identifier); Assert.AreEqual("op_UnaryPlus", od.Name); } + + [Test, Ignore("Parser crash")] + public void InvalidOperatorTrueDeclaration() + { + ParseUtilCSharp.ParseTypeMember("public static implicit operator true(MyBool b) {}", expectErrors: true); + } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConditionalOperatorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConditionalOperatorTests.cs index 9c8fe4cd97..e04499e30b 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConditionalOperatorTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConditionalOperatorTests.cs @@ -165,5 +165,37 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver AssertType(typeof(StringComparison), resolver.ResolveConditional( MakeResult(typeof(bool)), MakeResult(typeof(StringComparison)), MakeConstant(0))); } + + [Test] + public void TypeWithImplicitConversionToBool() + { + string program = @"struct MyBool { + public static implicit operator bool(MyBool b) {} + void Test() { + var x = $this$ ? 1 : 0; + } +}"; + Assert.AreEqual("System.Boolean", GetExpectedType(program).ReflectionName); + Conversion c = GetConversion(program); + Assert.IsTrue(c.IsValid); + Assert.IsTrue(c.IsUserDefined); + Assert.AreEqual("op_Implicit", c.Method.Name); + } + + [Test] + public void TypeWithOperatorTrue() + { + string program = @"struct MyBool { + public static bool operator true(MyBool b) {} + void Test() { + var x = $this$ ? 1 : 0; + } +}"; + Assert.AreEqual("System.Boolean", GetExpectedType(program).ReflectionName); + Conversion c = GetConversion(program); + Assert.IsTrue(c.IsValid); + Assert.IsTrue(c.IsUserDefined); + Assert.AreEqual("op_True", c.Method.Name); + } } }