Browse Source

Merge branch 'user-defined-operators' of git://github.com/erik-kallen/NRefactory

newNRvisualizers
Daniel Grunwald 13 years ago
parent
commit
259b5ba111
  1. 104
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs
  2. 159
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs
  3. 196
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs

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

@ -744,27 +744,113 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -744,27 +744,113 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return a.Kind != TypeKind.Interface && b.Kind != TypeKind.Interface
&& (StandardImplicitConversion(a, b).IsValid || StandardImplicitConversion(b, a).IsValid);
}
IType FindMostEncompassedType(IEnumerable<IType> candidates)
{
IType best = null;
foreach (var current in candidates) {
if (best == null || IsEncompassedBy(current, best))
best = current;
else if (!IsEncompassedBy(best, current))
return null; // Ambiguous
}
return best;
}
IType FindMostEncompassingType(IEnumerable<IType> candidates)
{
IType best = null;
foreach (var current in candidates) {
if (best == null || IsEncompassedBy(best, current))
best = current;
else if (!IsEncompassedBy(current, best))
return null; // Ambiguous
}
return best;
}
OperatorInfo SelectOperator(IType mostSpecificSource, IType mostSpecificTarget, IList<OperatorInfo> operators)
{
var selected = operators.Where(op => op.SourceType.Equals(mostSpecificSource) && op.TargetType.Equals(mostSpecificTarget)).ToList();
//TODO: This is fishy. I can't find the justification for this in the spec, but when performing algorithm in the spec for the conversion DateTime -> DateTimeOffset? (test UserDefinedImplicitNullableConversion)
// we get (according to the algorithm in §6.4.4, if my understanding is correct):
// S0 = DateTime, T0 = DateTimeOffset
// U = { DateTime -> DateTimeOffset (user-defined); DateTime? -> DateTimeOffset? (lifted) }
// Sx = DateTime
// Tx = DateTimeOffset?
// Now the problem is in the next step:
// "* If U contains exactly one user-defined conversion operator that converts from Sx to Tx, then this is the most specific conversion operator". Nope, no conversion DateTime -> DateTimeOffset?
// "* Otherwise, if U contains exactly one lifted conversion operator that converts from Sx to Tx, then this is the most specific conversion operator". Nope, no lifted conversion DateTime -> DateTimeOffset?
// "* Otherwise, the conversion is ambiguous and a compile-time error occurs". This is what happens!
if (selected.Count == 0 && NullableType.IsNullable(mostSpecificTarget))
selected = operators.Where(op => op.SourceType.Equals(mostSpecificSource) && op.TargetType.Equals(NullableType.GetUnderlyingType(mostSpecificTarget))).ToList();
int nNonLifted = selected.Count(s => !s.IsLifted);
if (nNonLifted == 1)
return selected.First(s => !s.IsLifted);
else if (nNonLifted > 1)
return null; // Ambiguous
int nLifted = selected.Count(s => s.IsLifted);
if (nLifted == 1)
return selected.First(s => s.IsLifted);
else
return null; // Ambiguous or none applicable.
}
Conversion UserDefinedImplicitConversion(IType fromType, IType toType)
{
// C# 4.0 spec §6.4.4 User-defined implicit conversions
var operators = GetApplicableConversionOperators(fromType, toType, false);
// TODO: Find most specific conversion
if (operators.Count > 0)
return Conversion.UserDefinedImplicitConversion(operators[0].Method, operators[0].IsLifted);
else
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.None;
var mostSpecificTarget = operators.Any(op => op.TargetType.Equals(toType)) ? toType : FindMostEncompassingType(operators.Select(op => op.TargetType));
if (mostSpecificTarget == null)
return Conversion.None;
var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators);
return selected != null ? Conversion.UserDefinedImplicitConversion(selected.Method, selected.IsLifted) : Conversion.None;
}
else {
return Conversion.None;
}
}
Conversion UserDefinedExplicitConversion(IType fromType, IType toType)
{
// C# 4.0 spec §6.4.5 User-defined implicit conversions
var operators = GetApplicableConversionOperators(fromType, toType, true);
// TODO: Find most specific conversion
if (operators.Count > 0)
return Conversion.UserDefinedExplicitConversion(operators[0].Method, operators[0].IsLifted);
else
if (operators.Count > 0) {
IType mostSpecificSource;
if (operators.Any(op => op.SourceType.Equals(fromType)))
mostSpecificSource = fromType;
else if (operators.Any(op => IsEncompassedBy(fromType, op.SourceType)))
mostSpecificSource = FindMostEncompassedType(operators.Where(op => IsEncompassedBy(fromType, op.SourceType)).Select(op => op.SourceType));
else
mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType));
if (mostSpecificSource == null)
return Conversion.None;
IType mostSpecificTarget;
if (operators.Any(op => op.TargetType.Equals(toType)))
mostSpecificTarget = toType;
else if (operators.Any(op => IsEncompassedBy(op.TargetType, toType)))
mostSpecificTarget = FindMostEncompassingType(operators.Where(op => IsEncompassedBy(op.TargetType, toType)).Select(op => op.TargetType));
else
mostSpecificTarget = FindMostEncompassedType(operators.Select(op => op.TargetType));
if (mostSpecificTarget == null)
return Conversion.None;
var selected = SelectOperator(mostSpecificSource, mostSpecificTarget, operators);
return selected != null ? Conversion.UserDefinedExplicitConversion(selected.Method, selected.IsLifted) : Conversion.None;
}
else {
return Conversion.None;
}
}
class OperatorInfo

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

@ -831,5 +831,164 @@ class Test { @@ -831,5 +831,164 @@ class Test {
Assert.IsTrue(c.IsMethodGroupConversion);
Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName);
}
[Test]
public void UserDefinedImplicitConversion_PicksExactSourceTypeIfPossible() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(int i) {return new Convertible(); }
public static implicit operator Convertible(short s) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $33$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("i", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_PicksMostEncompassedSourceType() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(long l) {return new Convertible(); }
public static implicit operator Convertible(uint ui) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $(ushort)33$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("ui", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_NoMostEncompassedSourceTypeIsInvalid() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(ulong l) {return new Convertible(); }
public static implicit operator Convertible(int ui) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $(ushort)33$;
}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
}
[Test]
public void UserDefinedImplicitConversion_PicksExactTargetTypeIfPossible() {
string program = @"using System;
class Convertible {
public static implicit operator int(Convertible i) {return 0; }
public static implicit operator short(Convertible s) {return 0; }
}
class Test {
public void M() {
int a = $new Convertible()$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("i", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_PicksMostEncompassingTargetType() {
string program = @"using System;
class Convertible {
public static implicit operator int(Convertible i) {return 0; }
public static implicit operator ushort(Convertible us) {return 0; }
}
class Test {
public void M() {
ulong a = $new Convertible()$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("us", c.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedImplicitConversion_NoMostEncompassingTargetTypeIsInvalid() {
string program = @"using System;
class Convertible {
public static implicit operator uint(Convertible i) {return 0; }
public static implicit operator short(Convertible us) {return 0; }
}
class Test {
public void M() {
long a = $new Convertible()$;
}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
}
[Test]
public void UserDefinedImplicitConversion_AmbiguousIsInvalid() {
string program = @"using System;
class Convertible1 {
public static implicit operator Convertible2(Convertible1 c) {return 0; }
}
class Convertible2 {
public static implicit operator Convertible2(Convertible1 c) {return 0; }
}
class Test {
public void M() {
Convertible2 a = $new Convertible1()$;
}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
}
[Test]
public void UserDefinedImplicitConversion_DefinedNullableTakesPrecedenceOverLifted() {
string program = @"using System;
struct Convertible {
public static implicit operator Convertible(int i) {return new Convertible(); }
public static implicit operator Convertible?(int? ni) {return new Convertible(); }
}
class Test {
public void M() {
Convertible? a = $(int?)33$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.IsFalse(c.IsLifted);
Assert.AreEqual("ni", c.Method.Parameters[0].Name);
}
[Test, Ignore("TODO: The 'most encompassing' algorithm should pick the long overload, but csc picks the uint one.")]
public void UserDefinedImplicitConversion_UIntConstant() {
string program = @"using System;
class Convertible {
public static implicit operator Convertible(long l) {return new Convertible(); }
public static implicit operator Convertible(uint ui) {return new Convertible(); }
}
class Test {
public void M() {
Convertible a = $33$;
}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsUserDefined);
Assert.AreEqual("ui", c.Method.Parameters[0].Name);
}
}
}

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

@ -509,7 +509,6 @@ class C { @@ -509,7 +509,6 @@ class C {
}
[Test]
[Ignore("Not implemented yet.")]
public void BothDirectConversionAndBaseClassConversionAvailable()
{
var rr = Resolve<ConversionResolveResult>(@"
@ -528,5 +527,200 @@ class C { @@ -528,5 +527,200 @@ class C {
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("b", rr.Conversion.Method.Parameters.Single().Name);
}
[Test]
public void UserDefinedExplicitConversion_PicksExactSourceTypeIfPossible() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(int i) {return new Convertible(); }
public static explicit operator Convertible(short s) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassedSourceTypeIfPossible() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(long l) {return new Convertible(); }
public static explicit operator Convertible(uint ui) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)(ushort)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassingSourceType() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(int i) {return new Convertible(); }
public static explicit operator Convertible(ushort us) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)(long)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_NoMostEncompassingSourceTypeIsInvalid() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(uint i) {return new Convertible(); }
public static explicit operator Convertible(short us) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)(long)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsFalse(rr.Conversion.IsValid);
}
[Test]
public void UserDefinedExplicitConversion_PicksExactTargetTypeIfPossible() {
string program = @"using System;
class Convertible {
public static explicit operator int(Convertible i) {return 0; }
public static explicit operator short(Convertible s) {return 0; }
}
class Test {
public void M() {
var a = $(int)new Convertible()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("i", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassingTargetTypeIfPossible() {
string program = @"using System;
class Convertible {
public static explicit operator int(Convertible i) {return 0; }
public static explicit operator ushort(Convertible us) {return 0; }
}
class Test {
public void M() {
var a = $(ulong)new Convertible()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("us", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_PicksMostEncompassedTargetType() {
string program = @"using System;
class Convertible {
public static explicit operator long(Convertible l) { return 0; }
public static explicit operator uint(Convertible ui) { return 0; }
}
class Test {
public void M() {
var a = $(ushort)new Convertible()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
}
[Test]
public void UserDefinedExplicitConversion_NoMostEncompassedTargetTypeIsInvalid() {
string program = @"using System;
class Convertible {
public static explicit operator ulong(Convertible l) { return 0; }
public static explicit operator int(Convertible ui) { return 0; }
}
class Test {
public void M() {
var a = $(ushort)new Convertible()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsFalse(rr.Conversion.IsValid);
}
[Test]
public void UserDefinedExplicitConversion_AmbiguousIsInvalid() {
string program = @"using System;
class Convertible1 {
public static explicit operator Convertible2(Convertible1 c) {return 0; }
}
class Convertible2 {
public static explicit operator Convertible2(Convertible1 c) {return 0; }
}
class Test {
public void M() {
var a = $(Convertible2)new Convertible1()$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsFalse(rr.Conversion.IsValid);
}
[Test]
public void UserDefinedExplicitConversion_DefinedNullableTakesPrecedenceOverLifted() {
string program = @"using System;
struct Convertible {
public static explicit operator Convertible(int i) {return new Convertible(); }
public static explicit operator Convertible?(int? ni) {return new Convertible(); }
}
class Test {
public void M() {
a = $(Convertible?)(int?)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.IsFalse(rr.Conversion.IsLifted);
Assert.AreEqual("ni", rr.Conversion.Method.Parameters[0].Name);
}
[Test, Ignore("TODO: The 'most encompassing' algorithm should pick the long overload, but csc picks the uint one.")]
public void UserDefinedExplicitConversion_UIntConstant() {
string program = @"using System;
class Convertible {
public static explicit operator Convertible(long l) {return new Convertible(); }
public static explicit operator Convertible(uint ui) {return new Convertible(); }
}
class Test {
public void M() {
var a = $(Convertible)33$;
}
}";
var rr = Resolve<ConversionResolveResult>(program);
Assert.IsTrue(rr.Conversion.IsValid);
Assert.IsTrue(rr.Conversion.IsUserDefined);
Assert.AreEqual("ui", rr.Conversion.Method.Parameters[0].Name);
}
}
}

Loading…
Cancel
Save