Browse Source

Fixed resolving compound assignment operators.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
9af3c040f2
  1. 34
      ICSharpCode.NRefactory.CSharp/Ast/Expressions/AssignmentExpression.cs
  2. 7
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs
  3. 34
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
  4. 1
      ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
  5. 40
      ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs
  6. 48
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs
  7. 5
      ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs
  8. 25
      ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs
  9. 39
      ICSharpCode.NRefactory/Semantics/OperatorResolveResult.cs

34
ICSharpCode.NRefactory.CSharp/Ast/Expressions/AssignmentExpression.cs

@ -117,6 +117,40 @@ namespace ICSharpCode.NRefactory.CSharp @@ -117,6 +117,40 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
/// <summary>
/// Gets the binary operator for the specified compound assignment operator.
/// Returns null if 'op' is not a compound assignment.
/// </summary>
public static BinaryOperatorType? GetCorrespondingBinaryOperator(AssignmentOperatorType op)
{
switch (op) {
case AssignmentOperatorType.Assign:
return null;
case AssignmentOperatorType.Add:
return BinaryOperatorType.Add;
case AssignmentOperatorType.Subtract:
return BinaryOperatorType.Subtract;
case AssignmentOperatorType.Multiply:
return BinaryOperatorType.Multiply;
case AssignmentOperatorType.Divide:
return BinaryOperatorType.Divide;
case AssignmentOperatorType.Modulus:
return BinaryOperatorType.Modulus;
case AssignmentOperatorType.ShiftLeft:
return BinaryOperatorType.ShiftLeft;
case AssignmentOperatorType.ShiftRight:
return BinaryOperatorType.ShiftRight;
case AssignmentOperatorType.BitwiseAnd:
return BinaryOperatorType.BitwiseAnd;
case AssignmentOperatorType.BitwiseOr:
return BinaryOperatorType.BitwiseOr;
case AssignmentOperatorType.ExclusiveOr:
return BinaryOperatorType.ExclusiveOr;
default:
throw new NotSupportedException("Invalid value for AssignmentOperatorType");
}
}
public static ExpressionType GetLinqNodeType(AssignmentOperatorType op, bool checkForOverflow)
{
switch (op) {

7
ICSharpCode.NRefactory.CSharp/Resolver/CSharpInvocationResolveResult.cs

@ -46,11 +46,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -46,11 +46,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// </summary>
public readonly bool IsExpandedForm;
/// <summary>
/// Gets whether this is a lifted operator invocation.
/// </summary>
public readonly bool IsLiftedOperatorInvocation;
readonly IList<int> argumentToParameterMap;
public CSharpInvocationResolveResult(
@ -59,7 +54,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -59,7 +54,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
OverloadResolutionErrors overloadResolutionErrors = OverloadResolutionErrors.None,
bool isExtensionMethodInvocation = false,
bool isExpandedForm = false,
bool isLiftedOperatorInvocation = false,
bool isDelegateInvocation = false,
IList<int> argumentToParameterMap = null)
: base(targetResult, member, arguments)
@ -67,7 +61,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -67,7 +61,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.OverloadResolutionErrors = overloadResolutionErrors;
this.IsExtensionMethodInvocation = isExtensionMethodInvocation;
this.IsExpandedForm = isExpandedForm;
this.IsLiftedOperatorInvocation = isLiftedOperatorInvocation;
this.IsDelegateInvocation = isDelegateInvocation;
this.argumentToParameterMap = argumentToParameterMap;
}

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

@ -364,7 +364,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -364,7 +364,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
userDefinedOperatorOR.AddCandidate(candidate);
}
if (userDefinedOperatorOR.FoundApplicableCandidate) {
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR);
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
}
expression = UnaryNumericPromotion(op, ref type, isNullable, expression);
@ -418,7 +418,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -418,7 +418,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (userDefinedOperatorOR.BestCandidate != null) {
// If there are any user-defined operators, prefer those over the built-in operators.
// It'll be a more informative error.
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR);
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, UnaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
} else if (builtinOperatorOR.BestCandidateAmbiguousWith != null) {
// If the best candidate is ambiguous, just use the input type instead
// of picking one of the ambiguous overloads.
@ -543,7 +543,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -543,7 +543,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
userDefinedOperatorOR.AddCandidate(candidate);
}
if (userDefinedOperatorOR.FoundApplicableCandidate) {
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR);
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
}
if (SpecialType.NullType.Equals(lhsType) && rhsType.IsReferenceType == false
@ -762,7 +762,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -762,7 +762,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// 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);
return CreateResolveResultForUserDefinedOperator(userDefinedOperatorOR, BinaryOperatorExpression.GetLinqNodeType(op, this.CheckForOverflow));
else
return new ErrorResolveResult(resultType);
} else if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && m.CanEvaluateAtCompileTime) {
@ -1142,9 +1142,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1142,9 +1142,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
}
ResolveResult CreateResolveResultForUserDefinedOperator(OverloadResolution r)
ResolveResult CreateResolveResultForUserDefinedOperator(OverloadResolution r, System.Linq.Expressions.ExpressionType operatorType)
{
return r.CreateResolveResult(null);
if (r.BestCandidateErrors != OverloadResolutionErrors.None)
return r.CreateResolveResult(null);
IMethod method = (IMethod)r.BestCandidate;
return new OperatorResolveResult(method.ReturnType, operatorType, method,
isLiftedOperator: method is OverloadResolution.ILiftedOperator,
operands: r.GetArgumentsWithConversions());
}
#endregion
@ -2123,5 +2128,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -2123,5 +2128,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
return new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), referencedType);
}
#region ResolveAssignment
public ResolveResult ResolveAssignment(AssignmentOperatorType op, ResolveResult lhs, ResolveResult rhs)
{
var linqOp = AssignmentExpression.GetLinqNodeType(op, this.CheckForOverflow);
var bop = AssignmentExpression.GetCorrespondingBinaryOperator(op);
if (bop == null) {
return new OperatorResolveResult(lhs.Type, linqOp, lhs, this.Convert(rhs, lhs.Type));
}
ResolveResult bopResult = ResolveBinaryOperator(bop.Value, lhs, rhs);
OperatorResolveResult opResult = bopResult as OperatorResolveResult;
if (opResult == null || opResult.Operands.Count != 2)
return bopResult;
return new OperatorResolveResult(lhs.Type, linqOp, opResult.UserDefinedOperatorMethod, opResult.IsLiftedOperator,
new [] { lhs, opResult.Operands[1] });
}
#endregion
}
}

1
ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs

@ -793,7 +793,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -793,7 +793,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.BestCandidateErrors,
this.IsExtensionMethodInvocation,
this.BestCandidateIsExpandedForm,
member is ILiftedOperator,
isDelegateInvocation: false,
argumentToParameterMap: this.GetArgumentToParameterMap());
}

40
ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs

@ -1055,11 +1055,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1055,11 +1055,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult IAstVisitor<object, ResolveResult>.VisitAssignmentExpression(AssignmentExpression assignmentExpression, object data)
{
if (resolverEnabled) {
ResolveResult left = Resolve(assignmentExpression.Left);
ResolveResult right = Resolve(assignmentExpression.Right);
ProcessConversion(assignmentExpression.Right, right, left.Type);
var op = AssignmentExpression.GetLinqNodeType(assignmentExpression.Operator, resolver.CheckForOverflow);
return new OperatorResolveResult(left.Type, op, left, right);
Expression left = assignmentExpression.Left;
Expression right = assignmentExpression.Right;
ResolveResult leftResult = Resolve(left);
ResolveResult rightResult = Resolve(right);
ResolveResult rr = resolver.ResolveAssignment(assignmentExpression.Operator, leftResult, rightResult);
ProcessConversionsInBinaryOperatorResult(left, right, rr);
return rr;
} else {
ScanChildren(assignmentExpression);
return null;
@ -1084,17 +1086,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1084,17 +1086,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
ResolveResult leftResult = Resolve(left);
ResolveResult rightResult = Resolve(right);
ResolveResult rr = resolver.ResolveBinaryOperator(binaryOperatorExpression.Operator, leftResult, rightResult);
OperatorResolveResult orr = rr as OperatorResolveResult;
if (orr != null && orr.Operands.Count == 2) {
ProcessConversionResult(left, orr.Operands[0] as ConversionResolveResult);
ProcessConversionResult(right, orr.Operands[1] as ConversionResolveResult);
} else {
InvocationResolveResult irr = rr as InvocationResolveResult;
if (irr != null && irr.Arguments.Count == 2) {
ProcessConversionResult(left, irr.Arguments[0] as ConversionResolveResult);
ProcessConversionResult(right, irr.Arguments[1] as ConversionResolveResult);
}
}
ProcessConversionsInBinaryOperatorResult(left, right, rr);
return rr;
} else {
ScanChildren(binaryOperatorExpression);
@ -1102,6 +1094,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1102,6 +1094,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
}
ResolveResult ProcessConversionsInBinaryOperatorResult(Expression left, Expression right, ResolveResult rr)
{
OperatorResolveResult orr = rr as OperatorResolveResult;
if (orr != null && orr.Operands.Count == 2) {
ProcessConversionResult(left, orr.Operands[0] as ConversionResolveResult);
ProcessConversionResult(right, orr.Operands[1] as ConversionResolveResult);
} else {
InvocationResolveResult irr = rr as InvocationResolveResult;
if (irr != null && irr.Arguments.Count == 2) {
ProcessConversionResult(left, irr.Arguments[0] as ConversionResolveResult);
ProcessConversionResult(right, irr.Arguments[1] as ConversionResolveResult);
}
}
return rr;
}
ResolveResult IAstVisitor<object, ResolveResult>.VisitCastExpression(CastExpression castExpression, object data)
{
if (resolverEnabled) {

48
ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs

@ -510,16 +510,16 @@ class Test { @@ -510,16 +510,16 @@ class Test {
}
}
";
var irr = Resolve<CSharpInvocationResolveResult>(program);
var irr = Resolve<OperatorResolveResult>(program);
Assert.IsFalse(irr.IsError);
Assert.IsTrue(irr.IsLiftedOperatorInvocation);
Assert.IsTrue(irr.Member is OverloadResolution.ILiftedOperator);
Assert.AreEqual("A.op_Addition", irr.Member.FullName);
Assert.IsTrue(irr.IsLiftedOperator);
Assert.IsTrue(irr.UserDefinedOperatorMethod is OverloadResolution.ILiftedOperator);
Assert.AreEqual("A.op_Addition", irr.UserDefinedOperatorMethod.FullName);
Assert.AreEqual("System.Nullable`1[[S]]", irr.Type.ReflectionName);
Assert.AreEqual("System.Nullable`1[[S]]", irr.Member.ReturnType.ReflectionName);
Assert.AreEqual("System.Nullable`1[[S]]", irr.UserDefinedOperatorMethod.ReturnType.ReflectionName);
Conversion lhsConv = ((ConversionResolveResult)irr.Arguments[0]).Conversion;
Conversion rhsConv = ((ConversionResolveResult)irr.Arguments[1]).Conversion;
Conversion lhsConv = ((ConversionResolveResult)irr.Operands[0]).Conversion;
Conversion rhsConv = ((ConversionResolveResult)irr.Operands[1]).Conversion;
Assert.AreEqual(Conversion.ImplicitNullableConversion, lhsConv);
Assert.IsTrue(rhsConv.IsUserDefined);
Assert.AreEqual("A.op_Implicit", rhsConv.Method.FullName);
@ -545,5 +545,39 @@ class Test { @@ -545,5 +545,39 @@ class Test {
Assert.AreEqual("A.op_Addition", irr.Member.FullName);
Assert.AreEqual("S", irr.Type.ReflectionName);
}
[Test]
public void CompoundAssign_String_Char()
{
string program = @"
class Test {
string text;
void Append(char c) {
$text += c$;
}
}";
var irr = Resolve<OperatorResolveResult>(program);
Assert.IsFalse(irr.IsError);
Assert.AreEqual(System.Linq.Expressions.ExpressionType.AddAssign, irr.OperatorType);
Assert.IsNull(irr.UserDefinedOperatorMethod);
Assert.AreEqual("System.String", irr.Type.ReflectionName);
}
[Test]
public void CompoundAssign_Byte_Literal1()
{
string program = @"
class Test {
byte c;
void Inc() {
$c += 1$;
}
}";
var irr = Resolve<OperatorResolveResult>(program);
Assert.IsFalse(irr.IsError);
Assert.AreEqual(System.Linq.Expressions.ExpressionType.AddAssign, irr.OperatorType);
Assert.IsNull(irr.UserDefinedOperatorMethod);
Assert.AreEqual("System.Byte", irr.Type.ReflectionName);
}
}
}

5
ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs

@ -146,7 +146,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase @@ -146,7 +146,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase
public class OuterGeneric<X>
{
public class Inner {}
public class Inner {
OuterGeneric<X> referenceToOuter;
public Inner(OuterGeneric<X> referenceToOuter) {}
}
public OuterGeneric<X>.Inner Field1;
public Inner Field2;

25
ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs

@ -529,6 +529,31 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -529,6 +529,31 @@ namespace ICSharpCode.NRefactory.TypeSystem
Assert.AreEqual("ICSharpCode.NRefactory.TypeSystem.TestCase.OuterGeneric`1+Inner[[ICSharpCode.NRefactory.TypeSystem.TestCase.OuterGeneric`1+Inner[[`0]]]]", field3.Type.ReflectionName);
}
[Test]
public void InnerClassInGenericClass_TypeParameterOwner()
{
ITypeDefinition type = GetTypeDefinition(typeof(OuterGeneric<>.Inner));
Assert.AreSame(type, type.TypeParameters[0].Owner);
}
[Test]
public void InnerClassInGenericClass_ReferencesTheOuterClass_Field()
{
ITypeDefinition type = GetTypeDefinition(typeof(OuterGeneric<>.Inner));
IField f = type.Fields.Single();
Assert.AreEqual("ICSharpCode.NRefactory.TypeSystem.TestCase.OuterGeneric`1[[`0]]", f.Type.ReflectionName);
Assert.AreSame(type, ((f.Type as ParameterizedType).TypeArguments[0] as ITypeParameter).Owner);
}
[Test]
public void InnerClassInGenericClass_ReferencesTheOuterClass_Parameter()
{
ITypeDefinition type = GetTypeDefinition(typeof(OuterGeneric<>.Inner));
IParameter p = type.Methods.Single(m => m.IsConstructor).Parameters.Single();
Assert.AreEqual("ICSharpCode.NRefactory.TypeSystem.TestCase.OuterGeneric`1[[`0]]", p.Type.ReflectionName);
Assert.AreSame(type, ((p.Type as ParameterizedType).TypeArguments[0] as ITypeParameter).Owner);
}
ResolveResult GetParamsAttributeArgument(int index)
{
ITypeDefinition type = GetTypeDefinition(typeof(ParamsAttribute)).GetDefinition();

39
ICSharpCode.NRefactory/Semantics/OperatorResolveResult.cs

@ -29,14 +29,27 @@ namespace ICSharpCode.NRefactory.Semantics @@ -29,14 +29,27 @@ namespace ICSharpCode.NRefactory.Semantics
public class OperatorResolveResult : ResolveResult
{
readonly ExpressionType operatorType;
readonly ResolveResult[] operands;
readonly IMethod userDefinedOperatorMethod;
readonly IList<ResolveResult> operands;
readonly bool isLiftedOperator;
public OperatorResolveResult(IType resultType, ExpressionType expressionType, params ResolveResult[] operands)
public OperatorResolveResult(IType resultType, ExpressionType operatorType, params ResolveResult[] operands)
: base(resultType)
{
if (operands == null)
throw new ArgumentNullException("arguments");
this.operatorType = expressionType;
throw new ArgumentNullException("operands");
this.operatorType = operatorType;
this.operands = operands;
}
public OperatorResolveResult(IType resultType, ExpressionType operatorType, IMethod userDefinedOperatorMethod, bool isLiftedOperator, IList<ResolveResult> operands)
: base(resultType)
{
if (operands == null)
throw new ArgumentNullException("operands");
this.operatorType = operatorType;
this.userDefinedOperatorMethod = userDefinedOperatorMethod;
this.isLiftedOperator = isLiftedOperator;
this.operands = operands;
}
@ -47,10 +60,28 @@ namespace ICSharpCode.NRefactory.Semantics @@ -47,10 +60,28 @@ namespace ICSharpCode.NRefactory.Semantics
get { return operatorType; }
}
/// <summary>
/// Gets the operands.
/// </summary>
public IList<ResolveResult> Operands {
get { return operands; }
}
/// <summary>
/// Gets the user defined operator method.
/// Returns null if this is a predefined operator.
/// </summary>
public IMethod UserDefinedOperatorMethod {
get { return userDefinedOperatorMethod; }
}
/// <summary>
/// Gets whether this is a lifted operator.
/// </summary>
public bool IsLiftedOperator {
get { return isLiftedOperator; }
}
public override IEnumerable<ResolveResult> GetChildResults()
{
return operands;

Loading…
Cancel
Save