8 changed files with 682 additions and 13 deletions
@ -0,0 +1,85 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using NUnit.Framework; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.CSharp.Resolver |
||||||
|
{ |
||||||
|
// assign short name to the fake reflection type
|
||||||
|
using dynamic = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Dynamic; |
||||||
|
|
||||||
|
[TestFixture] |
||||||
|
public class ConditionalOperatorTests : ResolverTestBase |
||||||
|
{ |
||||||
|
[Test] |
||||||
|
public void PickMoreGeneralOfTheTypes() |
||||||
|
{ |
||||||
|
AssertType(typeof(object), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(bool)), MakeResult(typeof(string)), MakeResult(typeof(object)))); |
||||||
|
AssertType(typeof(long), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(bool)), MakeResult(typeof(int)), MakeResult(typeof(long)))); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void Null() |
||||||
|
{ |
||||||
|
AssertType(typeof(string), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(bool)), MakeResult(typeof(string)), MakeConstant(null))); |
||||||
|
AssertType(typeof(string), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(bool)), MakeConstant(null), MakeResult(typeof(string)))); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void DynamicInArguments() |
||||||
|
{ |
||||||
|
AssertType(typeof(dynamic), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(bool)), MakeResult(typeof(dynamic)), MakeResult(typeof(double)))); |
||||||
|
|
||||||
|
AssertType(typeof(dynamic), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(bool)), MakeResult(typeof(double)), MakeResult(typeof(dynamic)))); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void DynamicInCondition() |
||||||
|
{ |
||||||
|
AssertType(typeof(double), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(dynamic)), MakeResult(typeof(float)), MakeResult(typeof(double)))); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void AllDynamic() |
||||||
|
{ |
||||||
|
AssertType(typeof(dynamic), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(dynamic)), MakeResult(typeof(dynamic)), MakeResult(typeof(dynamic)))); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void ListOfDynamicAndListOfObject() |
||||||
|
{ |
||||||
|
AssertError(typeof(List<dynamic>), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(bool)), MakeResult(typeof(List<object>)), MakeResult(typeof(List<dynamic>)))); |
||||||
|
|
||||||
|
AssertError(typeof(List<object>), resolver.ResolveConditional( |
||||||
|
MakeResult(typeof(bool)), MakeResult(typeof(List<dynamic>)), MakeResult(typeof(List<object>)))); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void Constant() |
||||||
|
{ |
||||||
|
AssertConstant(1L, resolver.ResolveConditional( |
||||||
|
MakeConstant(true), MakeConstant(1), MakeConstant(2L))); |
||||||
|
|
||||||
|
AssertConstant(2L, resolver.ResolveConditional( |
||||||
|
MakeConstant(false), MakeConstant(1), MakeConstant(2L))); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void NotConstantIfFalsePortionNotConstant() |
||||||
|
{ |
||||||
|
AssertType(typeof(long), resolver.ResolveConditional( |
||||||
|
MakeConstant(true), MakeConstant(1), MakeResult(typeof(long)))); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,391 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
||||||
|
using ICSharpCode.NRefactory.Utils; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.CSharp.Resolver.ConstantValues |
||||||
|
{ |
||||||
|
// Contains representations for constant C# expressions.
|
||||||
|
// We use these instead of storing the full AST to reduce the memory usage.
|
||||||
|
|
||||||
|
// The type system's SimpleConstantValue is used to represent PrimitiveExpressions.
|
||||||
|
|
||||||
|
public abstract class ConstantExpression : Immutable, IConstantValue |
||||||
|
{ |
||||||
|
public abstract ResolveResult Resolve(CSharpResolver resolver); |
||||||
|
|
||||||
|
public static ResolveResult Resolve(IConstantValue constantValue, CSharpResolver resolver) |
||||||
|
{ |
||||||
|
ConstantExpression expr = constantValue as ConstantExpression; |
||||||
|
if (expr != null) |
||||||
|
return expr.Resolve(resolver); |
||||||
|
else |
||||||
|
return new ConstantResolveResult(constantValue.GetValueType(resolver.Context), constantValue.GetValue(resolver.Context)); |
||||||
|
} |
||||||
|
|
||||||
|
public IType GetValueType(ITypeResolveContext context) |
||||||
|
{ |
||||||
|
return Resolve(new CSharpResolver(context)).Type; |
||||||
|
} |
||||||
|
|
||||||
|
public object GetValue(ITypeResolveContext context) |
||||||
|
{ |
||||||
|
return Resolve(new CSharpResolver(context)).ConstantValue; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class ConstantCast : ConstantExpression, ISupportsInterning |
||||||
|
{ |
||||||
|
ITypeReference targetType; |
||||||
|
IConstantValue expression; |
||||||
|
readonly bool checkForOverflow; |
||||||
|
|
||||||
|
public ConstantCast(ITypeReference targetType, IConstantValue expression, bool checkForOverflow) |
||||||
|
{ |
||||||
|
if (targetType == null) |
||||||
|
throw new ArgumentNullException("targetType"); |
||||||
|
if (expression == null) |
||||||
|
throw new ArgumentNullException("expression"); |
||||||
|
this.targetType = targetType; |
||||||
|
this.expression = expression; |
||||||
|
this.checkForOverflow = checkForOverflow; |
||||||
|
} |
||||||
|
|
||||||
|
public override ResolveResult Resolve(CSharpResolver resolver) |
||||||
|
{ |
||||||
|
ResolveResult rr = Resolve(expression, resolver); |
||||||
|
resolver.CheckForOverflow = checkForOverflow; |
||||||
|
return resolver.ResolveCast(targetType.Resolve(resolver.Context), rr); |
||||||
|
} |
||||||
|
|
||||||
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
||||||
|
{ |
||||||
|
targetType = provider.Intern(targetType); |
||||||
|
expression = provider.Intern(expression); |
||||||
|
} |
||||||
|
|
||||||
|
int ISupportsInterning.GetHashCodeForInterning() |
||||||
|
{ |
||||||
|
unchecked { |
||||||
|
return targetType.GetHashCode() + expression.GetHashCode() * 1018829 + (checkForOverflow ? 614811 : 7125912); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
||||||
|
{ |
||||||
|
ConstantCast cast = other as ConstantCast; |
||||||
|
return cast != null |
||||||
|
&& this.targetType == cast.targetType && this.expression == cast.expression |
||||||
|
&& this.checkForOverflow == cast.checkForOverflow; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class ConstantIdentifierReference : ConstantExpression, ISupportsInterning |
||||||
|
{ |
||||||
|
string identifier; |
||||||
|
IList<ITypeReference> typeArguments; |
||||||
|
|
||||||
|
public ConstantIdentifierReference(string identifier, IList<ITypeReference> typeArguments = null) |
||||||
|
{ |
||||||
|
if (identifier == null) |
||||||
|
throw new ArgumentNullException("identifier"); |
||||||
|
this.identifier = identifier; |
||||||
|
this.typeArguments = typeArguments; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public override ResolveResult Resolve(CSharpResolver resolver) |
||||||
|
{ |
||||||
|
return resolver.ResolveSimpleName(identifier, ResolveTypes(resolver, typeArguments)); |
||||||
|
} |
||||||
|
|
||||||
|
internal static IList<IType> ResolveTypes(CSharpResolver resolver, IList<ITypeReference> typeArguments) |
||||||
|
{ |
||||||
|
if (typeArguments == null) |
||||||
|
return EmptyList<IType>.Instance; |
||||||
|
IType[] types = new IType[typeArguments.Count]; |
||||||
|
for (int i = 0; i < types.Length; i++) { |
||||||
|
types[i] = typeArguments[i].Resolve(resolver.Context); |
||||||
|
} |
||||||
|
return types; |
||||||
|
} |
||||||
|
|
||||||
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
||||||
|
{ |
||||||
|
identifier = provider.Intern(identifier); |
||||||
|
typeArguments = provider.InternList(typeArguments); |
||||||
|
} |
||||||
|
|
||||||
|
int ISupportsInterning.GetHashCodeForInterning() |
||||||
|
{ |
||||||
|
unchecked { |
||||||
|
int hashCode = identifier.GetHashCode(); |
||||||
|
if (typeArguments != null) |
||||||
|
hashCode ^= typeArguments.GetHashCode(); |
||||||
|
return hashCode; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
||||||
|
{ |
||||||
|
ConstantIdentifierReference cir = other as ConstantIdentifierReference; |
||||||
|
return cir != null && |
||||||
|
this.identifier == cir.identifier && this.typeArguments == cir.typeArguments; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class ConstantMemberReference : ConstantExpression, ISupportsInterning |
||||||
|
{ |
||||||
|
ITypeReference targetType; |
||||||
|
IConstantValue targetExpression; |
||||||
|
string memberName; |
||||||
|
IList<ITypeReference> typeArguments; |
||||||
|
|
||||||
|
public ConstantMemberReference(ITypeReference targetType, string memberName, IList<ITypeReference> typeArguments = null) |
||||||
|
{ |
||||||
|
if (targetType == null) |
||||||
|
throw new ArgumentNullException("targetType"); |
||||||
|
if (memberName == null) |
||||||
|
throw new ArgumentNullException("memberName"); |
||||||
|
this.targetType = targetType; |
||||||
|
this.memberName = memberName; |
||||||
|
this.typeArguments = typeArguments; |
||||||
|
} |
||||||
|
|
||||||
|
public ConstantMemberReference(IConstantValue targetExpression, string memberName, IList<ITypeReference> typeArguments = null) |
||||||
|
{ |
||||||
|
if (targetExpression == null) |
||||||
|
throw new ArgumentNullException("targetExpression"); |
||||||
|
if (memberName == null) |
||||||
|
throw new ArgumentNullException("memberName"); |
||||||
|
this.targetExpression = targetExpression; |
||||||
|
this.memberName = memberName; |
||||||
|
this.typeArguments = typeArguments; |
||||||
|
} |
||||||
|
|
||||||
|
public override ResolveResult Resolve(CSharpResolver resolver) |
||||||
|
{ |
||||||
|
ResolveResult rr; |
||||||
|
if (targetType != null) |
||||||
|
rr = new TypeResolveResult(targetType.Resolve(resolver.Context)); |
||||||
|
else |
||||||
|
rr = Resolve(targetExpression, resolver); |
||||||
|
return resolver.ResolveMemberAccess(rr, memberName, ConstantIdentifierReference.ResolveTypes(resolver, typeArguments)); |
||||||
|
} |
||||||
|
|
||||||
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
||||||
|
{ |
||||||
|
targetType = provider.Intern(targetType); |
||||||
|
targetExpression = provider.Intern(targetExpression); |
||||||
|
memberName = provider.Intern(memberName); |
||||||
|
typeArguments = provider.InternList(typeArguments); |
||||||
|
} |
||||||
|
|
||||||
|
int ISupportsInterning.GetHashCodeForInterning() |
||||||
|
{ |
||||||
|
unchecked { |
||||||
|
int hashCode; |
||||||
|
if (targetType != null) |
||||||
|
hashCode = targetType.GetHashCode(); |
||||||
|
else |
||||||
|
hashCode = targetExpression.GetHashCode(); |
||||||
|
hashCode ^= memberName.GetHashCode(); |
||||||
|
if (typeArguments != null) |
||||||
|
hashCode ^= typeArguments.GetHashCode(); |
||||||
|
return hashCode; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
||||||
|
{ |
||||||
|
ConstantMemberReference cmr = other as ConstantMemberReference; |
||||||
|
return cmr != null |
||||||
|
&& this.targetType == cmr.targetType && this.targetExpression == cmr.targetExpression |
||||||
|
&& this.memberName == cmr.memberName && this.typeArguments == cmr.typeArguments; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class ConstantDefaultValue : ConstantExpression, ISupportsInterning |
||||||
|
{ |
||||||
|
ITypeReference type; |
||||||
|
|
||||||
|
public ConstantDefaultValue(ITypeReference type) |
||||||
|
{ |
||||||
|
if (type == null) |
||||||
|
throw new ArgumentNullException("type"); |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
public override ResolveResult Resolve(CSharpResolver resolver) |
||||||
|
{ |
||||||
|
return resolver.ResolveDefaultValue(type.Resolve(resolver.Context)); |
||||||
|
} |
||||||
|
|
||||||
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
||||||
|
{ |
||||||
|
type = provider.Intern(type); |
||||||
|
} |
||||||
|
|
||||||
|
int ISupportsInterning.GetHashCodeForInterning() |
||||||
|
{ |
||||||
|
return type.GetHashCode(); |
||||||
|
} |
||||||
|
|
||||||
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
||||||
|
{ |
||||||
|
ConstantDefaultValue o = other as ConstantDefaultValue; |
||||||
|
return o != null && this.type == o.type; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class ConstantUnaryOperator : ConstantExpression, ISupportsInterning |
||||||
|
{ |
||||||
|
UnaryOperatorType operatorType; |
||||||
|
IConstantValue expression; |
||||||
|
bool checkForOverflow; |
||||||
|
|
||||||
|
public ConstantUnaryOperator(UnaryOperatorType operatorType, IConstantValue expression, bool checkForOverflow) |
||||||
|
{ |
||||||
|
if (expression == null) |
||||||
|
throw new ArgumentNullException("expression"); |
||||||
|
this.operatorType = operatorType; |
||||||
|
this.expression = expression; |
||||||
|
this.checkForOverflow = checkForOverflow; |
||||||
|
} |
||||||
|
|
||||||
|
public override ResolveResult Resolve(CSharpResolver resolver) |
||||||
|
{ |
||||||
|
ResolveResult rr = Resolve(expression, resolver); |
||||||
|
resolver.CheckForOverflow = checkForOverflow; |
||||||
|
return resolver.ResolveUnaryOperator(operatorType, rr); |
||||||
|
} |
||||||
|
|
||||||
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
||||||
|
{ |
||||||
|
expression = provider.Intern(expression); |
||||||
|
} |
||||||
|
|
||||||
|
int ISupportsInterning.GetHashCodeForInterning() |
||||||
|
{ |
||||||
|
unchecked { |
||||||
|
return expression.GetHashCode() * 811 + operatorType.GetHashCode() + (checkForOverflow ? 1717211 : 12751265); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
||||||
|
{ |
||||||
|
ConstantUnaryOperator uop = other as ConstantUnaryOperator; |
||||||
|
return uop != null |
||||||
|
&& this.operatorType == uop.operatorType |
||||||
|
&& this.expression == uop.expression |
||||||
|
&& this.checkForOverflow == uop.checkForOverflow; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class ConstantBinaryOperator : ConstantExpression, ISupportsInterning |
||||||
|
{ |
||||||
|
IConstantValue left; |
||||||
|
BinaryOperatorType operatorType; |
||||||
|
IConstantValue right; |
||||||
|
bool checkForOverflow; |
||||||
|
|
||||||
|
public ConstantBinaryOperator(IConstantValue left, BinaryOperatorType operatorType, IConstantValue right, bool checkForOverflow) |
||||||
|
{ |
||||||
|
if (left == null) |
||||||
|
throw new ArgumentNullException("left"); |
||||||
|
if (right == null) |
||||||
|
throw new ArgumentNullException("right"); |
||||||
|
this.left = left; |
||||||
|
this.operatorType = operatorType; |
||||||
|
this.right = right; |
||||||
|
this.checkForOverflow = checkForOverflow; |
||||||
|
} |
||||||
|
|
||||||
|
public override ResolveResult Resolve(CSharpResolver resolver) |
||||||
|
{ |
||||||
|
ResolveResult lhs = Resolve(left, resolver); |
||||||
|
ResolveResult rhs = Resolve(right, resolver); |
||||||
|
resolver.CheckForOverflow = checkForOverflow; |
||||||
|
return resolver.ResolveBinaryOperator(operatorType, lhs, rhs); |
||||||
|
} |
||||||
|
|
||||||
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
||||||
|
{ |
||||||
|
left = provider.Intern(left); |
||||||
|
right = provider.Intern(right); |
||||||
|
} |
||||||
|
|
||||||
|
int ISupportsInterning.GetHashCodeForInterning() |
||||||
|
{ |
||||||
|
unchecked { |
||||||
|
return left.GetHashCode() * 811 + operatorType.GetHashCode() |
||||||
|
+ right.GetHashCode() * 91781 + (checkForOverflow ? 1261561 : 174811); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
||||||
|
{ |
||||||
|
ConstantBinaryOperator bop = other as ConstantBinaryOperator; |
||||||
|
return bop != null |
||||||
|
&& this.operatorType == bop.operatorType |
||||||
|
&& this.left == bop.left && this.right == bop.right |
||||||
|
&& this.checkForOverflow == bop.checkForOverflow; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class ConstantConditionalOperator : ConstantExpression, ISupportsInterning |
||||||
|
{ |
||||||
|
IConstantValue condition, trueExpr, falseExpr; |
||||||
|
|
||||||
|
public ConstantConditionalOperator(IConstantValue condition, IConstantValue trueExpr, IConstantValue falseExpr) |
||||||
|
{ |
||||||
|
if (condition == null) |
||||||
|
throw new ArgumentNullException("condition"); |
||||||
|
if (trueExpr == null) |
||||||
|
throw new ArgumentNullException("trueExpr"); |
||||||
|
if (falseExpr == null) |
||||||
|
throw new ArgumentNullException("falseExpr"); |
||||||
|
this.condition = condition; |
||||||
|
this.trueExpr = trueExpr; |
||||||
|
this.falseExpr = falseExpr; |
||||||
|
} |
||||||
|
|
||||||
|
public override ResolveResult Resolve(CSharpResolver resolver) |
||||||
|
{ |
||||||
|
return resolver.ResolveConditional( |
||||||
|
Resolve(condition, resolver), |
||||||
|
Resolve(trueExpr, resolver), |
||||||
|
Resolve(falseExpr, resolver) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
||||||
|
{ |
||||||
|
condition = provider.Intern(condition); |
||||||
|
trueExpr = provider.Intern(trueExpr); |
||||||
|
falseExpr = provider.Intern(falseExpr); |
||||||
|
} |
||||||
|
|
||||||
|
int ISupportsInterning.GetHashCodeForInterning() |
||||||
|
{ |
||||||
|
unchecked { |
||||||
|
return condition.GetHashCode() * 182981713 |
||||||
|
+ trueExpr.GetHashCode() * 917517169 |
||||||
|
+ falseExpr.GetHashCode() * 611651; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
||||||
|
{ |
||||||
|
ConstantConditionalOperator coo = other as ConstantConditionalOperator; |
||||||
|
return coo != null |
||||||
|
&& this.condition == coo.condition |
||||||
|
&& this.trueExpr == coo.trueExpr |
||||||
|
&& this.falseExpr == coo.falseExpr; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue