Browse Source

Merge pull request #137 from erik-kallen/conversion-improvements

Added information about built-in conversions before and after a user-defined conversion operator is applied
pull/32/merge
Daniel Grunwald 13 years ago
parent
commit
97c267441a
  1. 20
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs
  2. 4
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
  3. 39
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs
  4. 39
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs
  5. 40
      ICSharpCode.NRefactory/Semantics/Conversion.cs

20
ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs

@ -793,22 +793,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -793,22 +793,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return best;
}
Conversion SelectOperator(IType mostSpecificSource, IType mostSpecificTarget, IList<OperatorInfo> operators, bool isImplicit)
Conversion SelectOperator(IType mostSpecificSource, IType mostSpecificTarget, IList<OperatorInfo> operators, bool isImplicit, IType source, IType target)
{
var selected = operators.Where(op => op.SourceType.Equals(mostSpecificSource) && op.TargetType.Equals(mostSpecificTarget)).ToList();
if (selected.Count == 0)
return Conversion.None;
if (selected.Count == 1)
return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit);
return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target));
int nNonLifted = selected.Count(s => !s.IsLifted);
if (nNonLifted == 1) {
var op = selected.First(s => !s.IsLifted);
return Conversion.UserDefinedConversion(op.Method, isLifted: op.IsLifted, isImplicit: isImplicit);
return Conversion.UserDefinedConversion(op.Method, isLifted: op.IsLifted, isImplicit: isImplicit, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target));
}
return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, isAmbiguous: true);
return Conversion.UserDefinedConversion(selected[0].Method, isLifted: selected[0].IsLifted, isImplicit: isImplicit, isAmbiguous: true, conversionBeforeUserDefinedOperator: ExplicitConversion(source, mostSpecificSource), conversionAfterUserDefinedOperator: ExplicitConversion(mostSpecificTarget, target));
}
Conversion UserDefinedImplicitConversion(ResolveResult fromResult, IType fromType, IType toType)
@ -819,16 +819,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -819,16 +819,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (operators.Count > 0) {
var mostSpecificSource = operators.Any(op => op.SourceType.Equals(fromType)) ? fromType : FindMostEncompassedType(operators.Select(op => op.SourceType));
if (mostSpecificSource == null)
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true);
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
var mostSpecificTarget = operators.Any(op => op.TargetType.Equals(toType)) ? toType : FindMostEncompassingType(operators.Select(op => op.TargetType));
if (mostSpecificTarget == null) {
if (NullableType.IsNullable(toType))
return UserDefinedImplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
else
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true);
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: true, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
}
var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, true);
var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, true, fromType, toType);
if (selected != Conversion.None) {
if (selected.IsLifted && NullableType.IsNullable(toType)) {
// Prefer A -> B -> B? over A -> A? -> B?
@ -864,7 +864,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -864,7 +864,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType));
}
if (mostSpecificSource == null)
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true);
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
IType mostSpecificTarget;
if (operators.Any(op => op.TargetType.Equals(toType)))
@ -877,10 +877,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -877,10 +877,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (NullableType.IsNullable(toType))
return UserDefinedExplicitConversion(fromResult, fromType, NullableType.GetUnderlyingType(toType));
else
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true);
return Conversion.UserDefinedConversion(operators[0].Method, isImplicit: false, isLifted: operators[0].IsLifted, isAmbiguous: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
}
var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, false);
var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators, false, fromType, toType);
if (selected != Conversion.None) {
if (selected.IsLifted && NullableType.IsNullable(toType)) {
// Prefer A -> B -> B? over A -> A? -> B?

4
ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs

@ -2311,7 +2311,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -2311,7 +2311,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (!c.IsValid) {
var opTrue = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_True").FirstOrDefault();
if (opTrue != null) {
c = Conversion.UserDefinedConversion(opTrue, isImplicit: true);
c = Conversion.UserDefinedConversion(opTrue, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
}
}
return Convert(input, boolean, c);
@ -2331,7 +2331,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -2331,7 +2331,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (!c.IsValid) {
var opFalse = input.Type.GetMethods(m => m.IsOperator && m.Name == "op_False").FirstOrDefault();
if (opFalse != null) {
c = Conversion.UserDefinedConversion(opFalse, isImplicit: true);
c = Conversion.UserDefinedConversion(opFalse, isImplicit: true, conversionBeforeUserDefinedOperator: Conversion.None, conversionAfterUserDefinedOperator: Conversion.None);
return Convert(input, boolean, c);
}
}

39
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs

@ -1251,5 +1251,44 @@ class Test { @@ -1251,5 +1251,44 @@ class Test {
Assert.IsTrue(c.IsUserDefined);
Assert.IsFalse(c.IsValid);
}
[Test]
public void UserDefinedImplicitConversion_ConversionBeforeUserDefinedOperatorIsCorrect() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(long l) {return new Convertible(); }
}
class Test {
public void M() {
int i = 33;
Convertible a = $i$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.ConversionBeforeUserDefinedOperator.IsImplicit);
Assert.IsTrue(c.ConversionBeforeUserDefinedOperator.IsNumericConversion);
Assert.IsTrue(c.ConversionBeforeUserDefinedOperator.IsValid);
Assert.IsTrue(c.ConversionAfterUserDefinedOperator.IsIdentityConversion);
}
[Test]
public void UserDefinedImplicitConversion_ConversionAfterUserDefinedOperatorIsCorrect() {
string program = @"using System;
class Convertible {
public static implicit operator int(Convertible i) {return 0; }
}
class Test {
public void M() {
long a = $new Convertible()$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.ConversionBeforeUserDefinedOperator.IsIdentityConversion);
Assert.IsTrue(c.ConversionAfterUserDefinedOperator.IsImplicit);
Assert.IsTrue(c.ConversionAfterUserDefinedOperator.IsNumericConversion);
Assert.IsTrue(c.ConversionAfterUserDefinedOperator.IsValid);
}
}
}

39
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs

@ -870,5 +870,44 @@ class Test { @@ -870,5 +870,44 @@ class Test {
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("ci", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_ConversionBeforeUserDefinedOperatorIsCorrect() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(int l) {return new Convertible(); }
}
class Test {
public void M() {
long i = 33;
Convertible a = $(Convertible)i$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.ConversionBeforeUserDefinedOperator.IsValid);
Assert.IsTrue(rr.Conversion.ConversionBeforeUserDefinedOperator.IsExplicit);
Assert.IsTrue(rr.Conversion.ConversionBeforeUserDefinedOperator.IsNumericConversion);
Assert.IsTrue(rr.Conversion.ConversionAfterUserDefinedOperator.IsIdentityConversion);
}
[Test]
public void UserDefinedExplicitConversion_ConversionAfterUserDefinedOperatorIsCorrect() {
string program = @"using System;
class Convertible {
public static implicit operator long(Convertible i) {return 0; }
}
class Test {
public void M() {
int a = $(int)new Convertible()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.ConversionBeforeUserDefinedOperator.IsIdentityConversion);
Assert.IsTrue(rr.Conversion.ConversionAfterUserDefinedOperator.IsValid);
Assert.IsTrue(rr.Conversion.ConversionAfterUserDefinedOperator.IsExplicit);
Assert.IsTrue(rr.Conversion.ConversionAfterUserDefinedOperator.IsNumericConversion);
}
}
}

40
ICSharpCode.NRefactory/Semantics/Conversion.cs

@ -75,26 +75,26 @@ namespace ICSharpCode.NRefactory.Semantics @@ -75,26 +75,26 @@ namespace ICSharpCode.NRefactory.Semantics
public static readonly Conversion TryCast = new BuiltinConversion(false, 9);
[Obsolete("Use UserDefinedConversion() instead")]
public static Conversion UserDefinedImplicitConversion(IMethod operatorMethod, bool isLifted)
public static Conversion UserDefinedImplicitConversion(IMethod operatorMethod, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted)
{
if (operatorMethod == null)
throw new ArgumentNullException("operatorMethod");
return new UserDefinedConv(true, operatorMethod, isLifted, false);
return new UserDefinedConv(true, operatorMethod, conversionBeforeUserDefinedOperator, conversionAfterUserDefinedOperator, isLifted, false);
}
[Obsolete("Use UserDefinedConversion() instead")]
public static Conversion UserDefinedExplicitConversion(IMethod operatorMethod, bool isLifted)
public static Conversion UserDefinedExplicitConversion(IMethod operatorMethod, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted)
{
if (operatorMethod == null)
throw new ArgumentNullException("operatorMethod");
return new UserDefinedConv(false, operatorMethod, isLifted, false);
return new UserDefinedConv(false, operatorMethod, conversionBeforeUserDefinedOperator, conversionAfterUserDefinedOperator, isLifted, false);
}
public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, bool isLifted = false, bool isAmbiguous = false)
public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false)
{
if (operatorMethod == null)
throw new ArgumentNullException("operatorMethod");
return new UserDefinedConv(isImplicit, operatorMethod, isLifted, isAmbiguous);
return new UserDefinedConv(isImplicit, operatorMethod, conversionBeforeUserDefinedOperator, conversionAfterUserDefinedOperator, isLifted, isAmbiguous);
}
public static Conversion MethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup)
@ -275,13 +275,17 @@ namespace ICSharpCode.NRefactory.Semantics @@ -275,13 +275,17 @@ namespace ICSharpCode.NRefactory.Semantics
{
readonly IMethod method;
readonly bool isLifted;
readonly Conversion conversionBeforeUserDefinedOperator;
readonly Conversion conversionAfterUserDefinedOperator;
readonly bool isImplicit;
readonly bool isValid;
public UserDefinedConv(bool isImplicit, IMethod method, bool isLifted, bool isAmbiguous)
public UserDefinedConv(bool isImplicit, IMethod method, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted, bool isAmbiguous)
{
this.method = method;
this.isLifted = isLifted;
this.conversionBeforeUserDefinedOperator = conversionBeforeUserDefinedOperator;
this.conversionAfterUserDefinedOperator = conversionAfterUserDefinedOperator;
this.isImplicit = isImplicit;
this.isValid = !isAmbiguous;
}
@ -306,6 +310,14 @@ namespace ICSharpCode.NRefactory.Semantics @@ -306,6 +310,14 @@ namespace ICSharpCode.NRefactory.Semantics
get { return true; }
}
public override Conversion ConversionBeforeUserDefinedOperator {
get { return conversionBeforeUserDefinedOperator; }
}
public override Conversion ConversionAfterUserDefinedOperator {
get { return conversionAfterUserDefinedOperator; }
}
public override IMethod Method {
get { return method; }
}
@ -457,6 +469,20 @@ namespace ICSharpCode.NRefactory.Semantics @@ -457,6 +469,20 @@ namespace ICSharpCode.NRefactory.Semantics
get { return false; }
}
/// <summary>
/// The conversion that is applied to the input before the user-defined conversion operator is invoked.
/// </summary>
public virtual Conversion ConversionBeforeUserDefinedOperator {
get { return null; }
}
/// <summary>
/// The conversion that is applied to the result of the user-defined conversion operator.
/// </summary>
public virtual Conversion ConversionAfterUserDefinedOperator {
get { return null; }
}
/// <summary>
/// Gets whether this conversion is a boxing conversion.
/// </summary>

Loading…
Cancel
Save