Browse Source

Merge commit 'e1552755b97863393b543603557590ad90d8ef98'

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
bad0fdb30f
  1. 2
      ICSharpCode.NRefactory.Tests/CSharp/AstStructureTests.cs
  2. 91
      ICSharpCode.NRefactory.Tests/CSharp/InsertParenthesesVisitorTests.cs
  3. 12
      ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/BinaryOperatorExpressionTests.cs
  4. 2
      ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/CastExpressionTests.cs
  5. 12
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs
  6. 3
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  7. 49
      ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs
  8. 10
      ICSharpCode.NRefactory/CSharp/Ast/Expressions/AssignmentExpression.cs
  9. 24
      ICSharpCode.NRefactory/CSharp/Ast/Expressions/BinaryOperatorExpression.cs
  10. 2
      ICSharpCode.NRefactory/CSharp/Ast/Expressions/CastExpression.cs
  11. 17
      ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs
  12. 10
      ICSharpCode.NRefactory/CSharp/Ast/Expressions/UnaryOperatorExpression.cs
  13. 9
      ICSharpCode.NRefactory/CSharp/Ast/PrimitiveType.cs
  14. 9
      ICSharpCode.NRefactory/CSharp/Ast/SimpleType.cs
  15. 7
      ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs
  16. 252
      ICSharpCode.NRefactory/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  17. 46
      ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs
  18. 4
      ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs
  19. 8
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  20. 2
      ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs
  21. 1
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

2
ICSharpCode.NRefactory.Tests/CSharp/AstStructureTest.cs → ICSharpCode.NRefactory.Tests/CSharp/AstStructureTests.cs

@ -8,7 +8,7 @@ using NUnit.Framework; @@ -8,7 +8,7 @@ using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp
{
[TestFixture]
public class AstStructureTest
public class AstStructureTests
{
[Test]
public void RolesAreStaticReadOnly()

91
ICSharpCode.NRefactory.Tests/CSharp/InsertParenthesesVisitorTests.cs

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
// 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.IO;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp
{
[TestFixture]
public class InsertParenthesesVisitorTests
{
string InsertReadable(Expression expr)
{
expr = expr.Clone();
expr.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }, null);
StringWriter w = new StringWriter();
expr.AcceptVisitor(new OutputVisitor(w, new CSharpFormattingPolicy()), null);
return w.ToString();
}
string InsertRequired(Expression expr)
{
expr = expr.Clone();
expr.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = false }, null);
StringWriter w = new StringWriter();
expr.AcceptVisitor(new OutputVisitor(w, new CSharpFormattingPolicy()), null);
return w.ToString();
}
[Test]
public void EqualityInAssignment()
{
Expression expr = new AssignmentExpression(
new IdentifierExpression("cond"),
new BinaryOperatorExpression(
new IdentifierExpression("a"),
BinaryOperatorType.Equality,
new IdentifierExpression("b")
)
);
Assert.AreEqual("cond = a == b", InsertRequired(expr));
Assert.AreEqual("cond = (a == b)", InsertReadable(expr));
}
[Test]
public void TrickyCast1()
{
Expression expr = new UnaryOperatorExpression(
UnaryOperatorType.Minus, new IdentifierExpression("a")
).CastTo(new PrimitiveType("int"));
Assert.AreEqual("(int)-a", InsertRequired(expr));
Assert.AreEqual("(int)(-a)", InsertReadable(expr));
}
[Test]
public void TrickyCast2()
{
Expression expr = new UnaryOperatorExpression(
UnaryOperatorType.Minus, new IdentifierExpression("a")
).CastTo(new SimpleType("MyType"));
Assert.AreEqual("(MyType)(-a)", InsertRequired(expr));
Assert.AreEqual("(MyType)(-a)", InsertReadable(expr));
}
[Test]
public void TrickyCast3()
{
Expression expr = new UnaryOperatorExpression(
UnaryOperatorType.Not, new IdentifierExpression("a")
).CastTo(new SimpleType("MyType"));
Assert.AreEqual("(MyType)!a", InsertRequired(expr));
Assert.AreEqual("(MyType)(!a)", InsertReadable(expr));
}
[Test]
public void CastAndInvoke()
{
Expression expr = new IdentifierExpression("a")
.CastTo(new PrimitiveType("string"))
.Member("Length");
Assert.AreEqual("((string)a).Length", InsertRequired(expr));
Assert.AreEqual("((string)a).Length", InsertReadable(expr));
}
}
}

12
ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/BinaryOperatorExpressionTests.cs

@ -69,9 +69,9 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression @@ -69,9 +69,9 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression
OperatorPrecedenceTest("==", BinaryOperatorType.Equality, "&", BinaryOperatorType.BitwiseAnd, false);
OperatorPrecedenceTest("&", BinaryOperatorType.BitwiseAnd, "^", BinaryOperatorType.ExclusiveOr, false);
OperatorPrecedenceTest("^", BinaryOperatorType.ExclusiveOr, "|", BinaryOperatorType.BitwiseOr, false);
OperatorPrecedenceTest("|", BinaryOperatorType.BitwiseOr, "&&", BinaryOperatorType.LogicalAnd, false);
OperatorPrecedenceTest("&&", BinaryOperatorType.LogicalAnd, "||", BinaryOperatorType.LogicalOr, false);
OperatorPrecedenceTest("||", BinaryOperatorType.LogicalOr, "??", BinaryOperatorType.NullCoalescing, false);
OperatorPrecedenceTest("|", BinaryOperatorType.BitwiseOr, "&&", BinaryOperatorType.ConditionalAnd, false);
OperatorPrecedenceTest("&&", BinaryOperatorType.ConditionalAnd, "||", BinaryOperatorType.ConditionalOr, false);
OperatorPrecedenceTest("||", BinaryOperatorType.ConditionalOr, "??", BinaryOperatorType.NullCoalescing, false);
}
#endregion
@ -116,13 +116,13 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression @@ -116,13 +116,13 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression
[Test]
public void LogicalAndTest()
{
TestBinaryOperatorExpressionTest("a && b", BinaryOperatorType.LogicalAnd);
TestBinaryOperatorExpressionTest("a && b", BinaryOperatorType.ConditionalAnd);
}
[Test]
public void LogicalOrTest()
{
TestBinaryOperatorExpressionTest("a || b", BinaryOperatorType.LogicalOr);
TestBinaryOperatorExpressionTest("a || b", BinaryOperatorType.ConditionalOr);
}
[Test]
@ -221,7 +221,7 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression @@ -221,7 +221,7 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression
{
const string expr = "i1 < 0 || i1 > (Count - 1)";
BinaryOperatorExpression boe = ParseUtilCSharp.ParseExpression<BinaryOperatorExpression>(expr);
Assert.AreEqual(BinaryOperatorType.LogicalOr, boe.Operator);
Assert.AreEqual(BinaryOperatorType.ConditionalOr, boe.Operator);
}
}
}

2
ICSharpCode.NRefactory.Tests/CSharp/Parser/Expression/CastExpressionTests.cs

@ -133,7 +133,7 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression @@ -133,7 +133,7 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.Expression
public void IntMaxValueToBigInt()
{
CastExpression ce = ParseUtilCSharp.ParseExpression<CastExpression>("(BigInt)int.MaxValue");
Assert.AreEqual("BigInt", ce.CastTo.ToString());
Assert.AreEqual("BigInt", ce.Type.ToString());
Assert.IsTrue(ce.Expression is MemberReferenceExpression);
}
}

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

@ -345,26 +345,26 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -345,26 +345,26 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public void LogicalAnd()
{
AssertConstant(true, resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalAnd, MakeConstant(true), MakeConstant(true)));
BinaryOperatorType.ConditionalAnd, MakeConstant(true), MakeConstant(true)));
AssertConstant(false, resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalAnd, MakeConstant(false), MakeConstant(true)));
BinaryOperatorType.ConditionalAnd, MakeConstant(false), MakeConstant(true)));
AssertError(typeof(bool), resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalAnd, MakeConstant(false), MakeResult(typeof(bool?))));
BinaryOperatorType.ConditionalAnd, MakeConstant(false), MakeResult(typeof(bool?))));
}
[Test]
public void LogicalOr()
{
AssertConstant(true, resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalOr, MakeConstant(false), MakeConstant(true)));
BinaryOperatorType.ConditionalOr, MakeConstant(false), MakeConstant(true)));
AssertConstant(false, resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalOr, MakeConstant(false), MakeConstant(false)));
BinaryOperatorType.ConditionalOr, MakeConstant(false), MakeConstant(false)));
AssertError(typeof(bool), resolver.ResolveBinaryOperator(
BinaryOperatorType.LogicalOr, MakeConstant(false), MakeResult(typeof(bool?))));
BinaryOperatorType.ConditionalOr, MakeConstant(false), MakeResult(typeof(bool?))));
}
[Test]

3
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -58,7 +58,8 @@ @@ -58,7 +58,8 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CSharp\AstStructureTest.cs" />
<Compile Include="CSharp\AstStructureTests.cs" />
<Compile Include="CSharp\InsertParenthesesVisitorTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\DelegateDeclarationTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\NamespaceDeclarationTests.cs" />
<Compile Include="CSharp\Parser\GeneralScope\TypeDeclarationTests.cs" />

49
ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs

@ -63,7 +63,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -63,7 +63,7 @@ namespace ICSharpCode.NRefactory.CSharp
AstNode nextSibling;
AstNode firstChild;
AstNode lastChild;
Role role = Roles.Root;
Role role = RootRole;
public abstract NodeType NodeType {
get;
@ -159,10 +159,10 @@ namespace ICSharpCode.NRefactory.CSharp @@ -159,10 +159,10 @@ namespace ICSharpCode.NRefactory.CSharp
protected void SetChildByRole<T>(Role<T> role, T newChild) where T : AstNode
{
AstNode oldChild = GetChildByRole(role);
if (oldChild != null)
oldChild.ReplaceWith(newChild);
else
if (oldChild.IsNull)
AddChild(newChild, role);
else
oldChild.ReplaceWith(newChild);
}
protected void SetChildrenByRole<T>(Role<T> role, IEnumerable<T> newChildren) where T : AstNode
@ -229,7 +229,12 @@ namespace ICSharpCode.NRefactory.CSharp @@ -229,7 +229,12 @@ namespace ICSharpCode.NRefactory.CSharp
throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling");
// No need to test for "Cannot add children to null nodes",
// as there isn't any valid nextSibling in null nodes.
InsertChildBeforeUnsafe(nextSibling, child, role);
}
void InsertChildBeforeUnsafe(AstNode nextSibling, AstNode child, Role role)
{
child.parent = this;
child.role = role;
child.nextSibling = nextSibling;
@ -286,6 +291,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -286,6 +291,9 @@ namespace ICSharpCode.NRefactory.CSharp
Remove();
return;
}
if (this.parent == null) {
throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node");
}
if (newNode.parent != null) {
// TODO: what if newNode is used within *this* tree?
// e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);"
@ -323,6 +331,32 @@ namespace ICSharpCode.NRefactory.CSharp @@ -323,6 +331,32 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
public AstNode ReplaceWith(Func<AstNode, AstNode> replaceFunction)
{
if (replaceFunction == null)
throw new ArgumentNullException("replaceFunction");
AstNode oldParent = parent;
AstNode oldSuccessor = nextSibling;
Role oldRole = role;
Remove();
AstNode replacement = replaceFunction(this);
if (oldSuccessor != null && oldSuccessor.parent != oldParent)
throw new InvalidOperationException("replace function changed nextSibling of node being replaced?");
if (!(replacement == null || replacement.IsNull)) {
if (replacement.parent != null)
throw new InvalidOperationException("replace function must return the root of a tree");
if (!oldRole.IsValid(replacement)) {
throw new InvalidOperationException (string.Format("The new node '{0}' is not valid in the role {1}", replacement.GetType().Name, oldRole.ToString()));
}
if (oldSuccessor != null)
oldParent.InsertChildBeforeUnsafe(oldSuccessor, replacement, oldRole);
else
oldParent.AddChildUnsafe(replacement, oldRole);
}
return replacement;
}
/// <summary>
/// Clones the whole subtree starting at this AST node.
/// </summary>
@ -526,12 +560,15 @@ namespace ICSharpCode.NRefactory.CSharp @@ -526,12 +560,15 @@ namespace ICSharpCode.NRefactory.CSharp
public abstract S AcceptVisitor<T, S> (AstVisitor<T, S> visitor, T data);
// the Root role must be available when creating the null nodes, so we can't put it in the Roles class
static readonly Role<AstNode> RootRole = new Role<AstNode>("Root");
public static class Roles
{
/// <summary>
/// Root of an abstract syntax tree.
/// </summary>
public static readonly Role<AstNode> Root = new Role<AstNode>("Root", CSharp.AstNode.Null);
public static readonly Role<AstNode> Root = RootRole;
// some pre defined constants for common roles
public static readonly Role<Identifier> Identifier = new Role<Identifier>("Identifier", CSharp.Identifier.Null);

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

@ -38,6 +38,16 @@ namespace ICSharpCode.NRefactory.CSharp @@ -38,6 +38,16 @@ namespace ICSharpCode.NRefactory.CSharp
public readonly static Role<CSharpTokenNode> OperatorRole = BinaryOperatorExpression.OperatorRole;
public readonly static Role<Expression> RightRole = BinaryOperatorExpression.RightRole;
public AssignmentExpression()
{
}
public AssignmentExpression(Expression left, Expression right)
{
this.Left = left;
this.Right = right;
}
public AssignmentOperatorType Operator {
get;
set;

24
ICSharpCode.NRefactory/CSharp/Ast/Expressions/BinaryOperatorExpression.cs

@ -37,6 +37,17 @@ namespace ICSharpCode.NRefactory.CSharp @@ -37,6 +37,17 @@ namespace ICSharpCode.NRefactory.CSharp
public readonly static Role<CSharpTokenNode> OperatorRole = new Role<CSharpTokenNode>("Operator", CSharpTokenNode.Null);
public readonly static Role<Expression> RightRole = new Role<Expression>("Right", Expression.Null);
public BinaryOperatorExpression()
{
}
public BinaryOperatorExpression(Expression left, BinaryOperatorType op, Expression right)
{
this.Left = left;
this.Operator = op;
this.Right = right;
}
public BinaryOperatorType Operator {
get;
set;
@ -68,9 +79,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -68,9 +79,9 @@ namespace ICSharpCode.NRefactory.CSharp
return "&";
case BinaryOperatorType.BitwiseOr:
return "|";
case BinaryOperatorType.LogicalAnd:
case BinaryOperatorType.ConditionalAnd:
return "&&";
case BinaryOperatorType.LogicalOr:
case BinaryOperatorType.ConditionalOr:
return "||";
case BinaryOperatorType.ExclusiveOr:
return "^";
@ -110,14 +121,19 @@ namespace ICSharpCode.NRefactory.CSharp @@ -110,14 +121,19 @@ namespace ICSharpCode.NRefactory.CSharp
public enum BinaryOperatorType
{
// We avoid 'logical or' on purpose, because it's not clear if that refers to the bitwise
// or to the short-circuiting (conditional) operator:
// MCS and old NRefactory used bitwise='|', logical='||'
// but the C# spec uses logical='|', conditional='||'
/// <summary>left &amp; right</summary>
BitwiseAnd,
/// <summary>left | right</summary>
BitwiseOr,
/// <summary>left &amp;&amp; right</summary>
LogicalAnd,
ConditionalAnd,
/// <summary>left || right</summary>
LogicalOr,
ConditionalOr,
/// <summary>left ^ right</summary>
ExclusiveOr,

2
ICSharpCode.NRefactory/CSharp/Ast/Expressions/CastExpression.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -35,7 +35,7 @@ namespace ICSharpCode.NRefactory.CSharp
get { return GetChildByRole (Roles.LPar); }
}
public AstType CastTo {
public AstType Type {
get { return GetChildByRole (Roles.Type); }
set { SetChildByRole (Roles.Type, value); }
}

17
ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs

@ -45,6 +45,13 @@ namespace ICSharpCode.NRefactory.CSharp @@ -45,6 +45,13 @@ namespace ICSharpCode.NRefactory.CSharp
return (Expression)base.Clone();
}
public Expression ReplaceWith(Func<Expression, Expression> replaceFunction)
{
if (replaceFunction == null)
throw new ArgumentNullException("replaceFunction");
return (Expression)base.ReplaceWith(node => replaceFunction((Expression)node));
}
#region Builder methods
/// <summary>
/// Builds an member reference expression using this expression as target.
@ -100,6 +107,16 @@ namespace ICSharpCode.NRefactory.CSharp @@ -100,6 +107,16 @@ namespace ICSharpCode.NRefactory.CSharp
Arguments = arguments
};
}
public CastExpression CastTo(AstType type)
{
return new CastExpression { Type = type, Expression = this };
}
public AsExpression CastAs(AstType type)
{
return new AsExpression { Type = type, Expression = this };
}
#endregion
}
}

10
ICSharpCode.NRefactory/CSharp/Ast/Expressions/UnaryOperatorExpression.cs

@ -35,6 +35,16 @@ namespace ICSharpCode.NRefactory.CSharp @@ -35,6 +35,16 @@ namespace ICSharpCode.NRefactory.CSharp
{
public readonly static Role<CSharpTokenNode> OperatorRole = BinaryOperatorExpression.OperatorRole;
public UnaryOperatorExpression()
{
}
public UnaryOperatorExpression(UnaryOperatorType op, Expression expression)
{
this.Operator = op;
this.Expression = expression;
}
public UnaryOperatorType Operator {
get;
set;

9
ICSharpCode.NRefactory/CSharp/Ast/PrimitiveType.cs

@ -34,6 +34,15 @@ namespace ICSharpCode.NRefactory.CSharp @@ -34,6 +34,15 @@ namespace ICSharpCode.NRefactory.CSharp
public string Keyword { get; set; }
public AstLocation Location { get; set; }
public PrimitiveType()
{
}
public PrimitiveType(string keyword)
{
this.Keyword = keyword;
}
public override AstLocation StartLocation {
get {
return Location;

9
ICSharpCode.NRefactory/CSharp/Ast/SimpleType.cs

@ -33,6 +33,15 @@ namespace ICSharpCode.NRefactory.CSharp @@ -33,6 +33,15 @@ namespace ICSharpCode.NRefactory.CSharp
{
public class SimpleType : AstType
{
public SimpleType()
{
}
public SimpleType(string identifier)
{
this.Identifier = identifier;
}
public string Identifier {
get {
return GetChildByRole (Roles.Identifier).Name;

7
ICSharpCode.NRefactory/CSharp/Ast/Statements/Statement.cs

@ -37,6 +37,13 @@ namespace ICSharpCode.NRefactory.CSharp @@ -37,6 +37,13 @@ namespace ICSharpCode.NRefactory.CSharp
return (Statement)base.Clone();
}
public Statement ReplaceWith(Func<Statement, Statement> replaceFunction)
{
if (replaceFunction == null)
throw new ArgumentNullException("replaceFunction");
return (Statement)base.ReplaceWith(node => replaceFunction((Statement)node));
}
public override NodeType NodeType {
get { return NodeType.Statement; }
}

252
ICSharpCode.NRefactory/CSharp/OutputVisitor/InsertParenthesesVisitor.cs

@ -0,0 +1,252 @@ @@ -0,0 +1,252 @@
// 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;
namespace ICSharpCode.NRefactory.CSharp
{
/// <summary>
/// Inserts the parentheses into the AST that are needed to ensure the AST can be printed correctly.
/// For example, if the AST contains
/// BinaryOperatorExpresson(2, Mul, BinaryOperatorExpression(1, Add, 1))); printing that AST
/// would incorrectly result in "2 * 1 + 1". By running InsertParenthesesVisitor, the necessary
/// parentheses are inserted: "2 * (1 + 1)".
/// </summary>
public class InsertParenthesesVisitor : DepthFirstAstVisitor<object, object>
{
/// <summary>
/// Gets/Sets whether the visitor should insert parentheses to make the code better looking.
/// If this property is false, it will insert parentheses only where strictly required by the language spec.
/// </summary>
public bool InsertParenthesesForReadability { get; set; }
const int Primary = 15;
const int Unary = 14;
const int RelationalAndTypeTesting = 10;
const int Equality = 9;
const int Conditional = 2;
const int AssignmentAndLambda = 1;
/// <summary>
/// Gets the row number in the C# 4.0 spec operator precedence table.
/// </summary>
static int GetPrecedence(Expression expr)
{
// Note: the operator precedence table on MSDN is incorrect
UnaryOperatorExpression uoe = expr as UnaryOperatorExpression;
if (uoe != null) {
if (uoe.Operator == UnaryOperatorType.PostDecrement || uoe.Operator == UnaryOperatorType.PostIncrement)
return Primary;
else
return Unary;
}
if (expr is CastExpression)
return Unary;
BinaryOperatorExpression boe = expr as BinaryOperatorExpression;
if (boe != null) {
switch (boe.Operator) {
case BinaryOperatorType.Multiply:
case BinaryOperatorType.Divide:
case BinaryOperatorType.Modulus:
return 13; // multiplicative
case BinaryOperatorType.Add:
case BinaryOperatorType.Subtract:
return 12; // additive
case BinaryOperatorType.ShiftLeft:
case BinaryOperatorType.ShiftRight:
return 11;
case BinaryOperatorType.GreaterThan:
case BinaryOperatorType.GreaterThanOrEqual:
case BinaryOperatorType.LessThan:
case BinaryOperatorType.LessThanOrEqual:
return RelationalAndTypeTesting;
case BinaryOperatorType.Equality:
case BinaryOperatorType.InEquality:
return Equality;
case BinaryOperatorType.BitwiseAnd:
return 8;
case BinaryOperatorType.ExclusiveOr:
return 7;
case BinaryOperatorType.BitwiseOr:
return 6;
case BinaryOperatorType.ConditionalAnd:
return 5;
case BinaryOperatorType.ConditionalOr:
return 4;
case BinaryOperatorType.NullCoalescing:
return 3;
default:
throw new NotSupportedException("Invalid value for BinaryOperatorType");
}
}
if (expr is IsExpression || expr is AsExpression)
return RelationalAndTypeTesting;
if (expr is ConditionalExpression)
return Conditional;
if (expr is AssignmentExpression || expr is LambdaExpression)
return AssignmentAndLambda;
// anything else: primary expression
return Primary;
}
/// <summary>
/// Parenthesizes the expression if it does not have the minimum required precedence.
/// </summary>
static void ParenthesizeIfRequired(Expression expr, int minimumPrecedence)
{
if (GetPrecedence(expr) < minimumPrecedence) {
Parenthesize(expr);
}
}
static void Parenthesize(Expression expr)
{
expr.ReplaceWith(e => new ParenthesizedExpression { Expression = e });
}
// Primary expressions
public override object VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data)
{
ParenthesizeIfRequired(memberReferenceExpression.Target, Primary);
return base.VisitMemberReferenceExpression(memberReferenceExpression, data);
}
public override object VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data)
{
ParenthesizeIfRequired(pointerReferenceExpression.Target, Primary);
return base.VisitPointerReferenceExpression(pointerReferenceExpression, data);
}
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
{
ParenthesizeIfRequired(invocationExpression.Target, Primary);
return base.VisitInvocationExpression(invocationExpression, data);
}
public override object VisitIndexerExpression(IndexerExpression indexerExpression, object data)
{
ParenthesizeIfRequired(indexerExpression.Target, Primary);
return base.VisitIndexerExpression(indexerExpression, data);
}
// Unary expressions
public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data)
{
ParenthesizeIfRequired(unaryOperatorExpression.Expression, Unary);
return base.VisitUnaryOperatorExpression(unaryOperatorExpression, data);
}
public override object VisitCastExpression(CastExpression castExpression, object data)
{
ParenthesizeIfRequired(castExpression.Expression, Unary);
// There's a nasty issue in the C# grammar: cast expressions including certain operators are ambiguous in some cases
// "(int)-1" is fine, but "(A)-b" is not a cast.
UnaryOperatorExpression uoe = castExpression.Expression as UnaryOperatorExpression;
if (InsertParenthesesForReadability) {
if (uoe != null)
Parenthesize(castExpression.Expression);
} else {
if (uoe != null && !(uoe.Operator == UnaryOperatorType.BitNot || uoe.Operator == UnaryOperatorType.Not)) {
if (TypeCanBeMisinterpretedAsExpression(castExpression.Type)) {
Parenthesize(castExpression.Expression);
}
}
}
return base.VisitCastExpression(castExpression, data);
}
static bool TypeCanBeMisinterpretedAsExpression(AstType type)
{
// SimpleTypes can always be misinterpreted as IdentifierExpressions
// MemberTypes can be misinterpreted as MemberReferenceExpressions if they don't use double colon
// PrimitiveTypes or ComposedTypes can never be misinterpreted as expressions.
MemberType mt = type as MemberType;
if (mt != null)
return !mt.IsDoubleColon;
else
return type is SimpleType;
}
// Binary Operators
public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data)
{
int precedence = GetPrecedence(binaryOperatorExpression);
if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) {
if (InsertParenthesesForReadability) {
ParenthesizeIfRequired(binaryOperatorExpression.Left, Primary);
ParenthesizeIfRequired(binaryOperatorExpression.Right, Primary);
} else {
// ?? is right-associate
ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence + 1);
ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence);
}
} else {
if (InsertParenthesesForReadability && precedence < Equality) {
ParenthesizeIfRequired(binaryOperatorExpression.Left, Equality);
ParenthesizeIfRequired(binaryOperatorExpression.Right, Equality);
} else {
// all other binary operators are left-associative
ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence);
ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence + 1);
}
}
return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
}
public override object VisitIsExpression(IsExpression isExpression, object data)
{
if (InsertParenthesesForReadability) {
// few people know the precedence of 'is', so always put parentheses in nice-looking mode.
ParenthesizeIfRequired(isExpression.Expression, Primary);
} else {
ParenthesizeIfRequired(isExpression.Expression, RelationalAndTypeTesting);
}
return base.VisitIsExpression(isExpression, data);
}
public override object VisitAsExpression(AsExpression asExpression, object data)
{
if (InsertParenthesesForReadability) {
// few people know the precedence of 'as', so always put parentheses in nice-looking mode.
ParenthesizeIfRequired(asExpression.Expression, Primary);
} else {
ParenthesizeIfRequired(asExpression.Expression, RelationalAndTypeTesting);
}
return base.VisitAsExpression(asExpression, data);
}
// Conditional operator
public override object VisitConditionalExpression(ConditionalExpression conditionalExpression, object data)
{
// Associativity here is a bit tricky:
// (a ? b : c ? d : e) == (a ? b : (c ? d : e))
// (a ? b ? c : d : e) == (a ? (b ? c : d) : e)
// Only ((a ? b : c) ? d : e) strictly needs the additional parentheses
if (InsertParenthesesForReadability) {
// Precedence of ?: can be confusing; so always put parentheses in nice-looking mode.
ParenthesizeIfRequired(conditionalExpression.Condition, Primary);
ParenthesizeIfRequired(conditionalExpression.TrueExpression, Primary);
ParenthesizeIfRequired(conditionalExpression.FalseExpression, Primary);
} else {
ParenthesizeIfRequired(conditionalExpression.Condition, Conditional + 1);
ParenthesizeIfRequired(conditionalExpression.TrueExpression, Conditional);
ParenthesizeIfRequired(conditionalExpression.FalseExpression, Conditional);
}
return base.VisitConditionalExpression(conditionalExpression, data);
}
public override object VisitAssignmentExpression(AssignmentExpression assignmentExpression, object data)
{
// assignment is right-associative
ParenthesizeIfRequired(assignmentExpression.Left, AssignmentAndLambda + 1);
if (InsertParenthesesForReadability) {
ParenthesizeIfRequired(assignmentExpression.Right, RelationalAndTypeTesting + 1);
} else {
ParenthesizeIfRequired(assignmentExpression.Right, AssignmentAndLambda);
}
return base.VisitAssignmentExpression(assignmentExpression, data);
}
// don't need to handle lambdas, they have lowest precedence and unambiguous associativity
}
}

46
ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -393,7 +393,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -393,7 +393,9 @@ namespace ICSharpCode.NRefactory.CSharp
{
StartNode(assignmentExpression);
assignmentExpression.Left.AcceptVisitor(this, data);
Space(policy.AroundAssignmentParentheses);
WriteToken(AssignmentExpression.GetOperatorSymbol(assignmentExpression.Operator), AssignmentExpression.OperatorRole);
Space(policy.AroundAssignmentParentheses);
assignmentExpression.Right.AcceptVisitor(this, data);
return EndNode(assignmentExpression);
}
@ -409,7 +411,49 @@ namespace ICSharpCode.NRefactory.CSharp @@ -409,7 +411,49 @@ namespace ICSharpCode.NRefactory.CSharp
{
StartNode(binaryOperatorExpression);
binaryOperatorExpression.Left.AcceptVisitor(this, data);
bool spacePolicy;
switch (binaryOperatorExpression.Operator) {
case BinaryOperatorType.BitwiseAnd:
case BinaryOperatorType.BitwiseOr:
case BinaryOperatorType.ExclusiveOr:
spacePolicy = policy.AroundBitwiseOperatorParentheses;
break;
case BinaryOperatorType.ConditionalAnd:
case BinaryOperatorType.ConditionalOr:
spacePolicy = policy.AroundLogicalOperatorParentheses;
break;
case BinaryOperatorType.GreaterThan:
case BinaryOperatorType.GreaterThanOrEqual:
case BinaryOperatorType.LessThanOrEqual:
case BinaryOperatorType.LessThan:
spacePolicy = policy.AroundRelationalOperatorParentheses;
break;
case BinaryOperatorType.Equality:
case BinaryOperatorType.InEquality:
spacePolicy = policy.AroundEqualityOperatorParentheses;
break;
case BinaryOperatorType.Add:
case BinaryOperatorType.Subtract:
spacePolicy = policy.AroundAdditiveOperatorParentheses;
break;
case BinaryOperatorType.Multiply:
case BinaryOperatorType.Divide:
case BinaryOperatorType.Modulus:
spacePolicy = policy.AroundMultiplicativeOperatorParentheses;
break;
case BinaryOperatorType.ShiftLeft:
case BinaryOperatorType.ShiftRight:
spacePolicy = policy.AroundShiftOperatorParentheses;
break;
case BinaryOperatorType.NullCoalescing:
spacePolicy = true;
break;
default:
throw new NotSupportedException("Invalid value for BinaryOperatorType");
}
Space(spacePolicy);
WriteToken(BinaryOperatorExpression.GetOperatorSymbol(binaryOperatorExpression.Operator), BinaryOperatorExpression.OperatorRole);
Space(spacePolicy);
binaryOperatorExpression.Right.AcceptVisitor(this, data);
return EndNode(binaryOperatorExpression);
}
@ -419,7 +463,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -419,7 +463,7 @@ namespace ICSharpCode.NRefactory.CSharp
StartNode(castExpression);
LPar();
Space(policy.WithinCastParentheses);
castExpression.CastTo.AcceptVisitor(this, data);
castExpression.Type.AcceptVisitor(this, data);
Space(policy.WithinCastParentheses);
RPar();
Space(policy.SpacesAfterTypecast);

4
ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs

@ -1693,11 +1693,11 @@ namespace ICSharpCode.NRefactory.CSharp @@ -1693,11 +1693,11 @@ namespace ICSharpCode.NRefactory.CSharp
result.Operator = BinaryOperatorType.BitwiseOr;
break;
case Binary.Operator.LogicalAnd:
result.Operator = BinaryOperatorType.LogicalAnd;
result.Operator = BinaryOperatorType.ConditionalAnd;
opLength = 2;
break;
case Binary.Operator.LogicalOr:
result.Operator = BinaryOperatorType.LogicalOr;
result.Operator = BinaryOperatorType.ConditionalOr;
opLength = 2;
break;
}

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

@ -602,9 +602,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -602,9 +602,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator.
// - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit.
// - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit
if (op == BinaryOperatorType.LogicalAnd) {
if (op == BinaryOperatorType.ConditionalAnd) {
overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseAnd);
} else if (op == BinaryOperatorType.LogicalOr) {
} else if (op == BinaryOperatorType.ConditionalOr) {
overloadableOperatorName = GetOverloadableOperatorName(BinaryOperatorType.BitwiseOr);
} else if (op == BinaryOperatorType.NullCoalescing) {
// null coalescing operator is not overloadable and needs to be handled separately
@ -780,10 +780,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -780,10 +780,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
}
break;
case BinaryOperatorType.LogicalAnd:
case BinaryOperatorType.ConditionalAnd:
methodGroup = logicalAndOperator;
break;
case BinaryOperatorType.LogicalOr:
case BinaryOperatorType.ConditionalOr:
methodGroup = logicalOrOperator;
break;
default:

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

@ -541,7 +541,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -541,7 +541,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public override ResolveResult VisitCastExpression(CastExpression castExpression, object data)
{
if (resolverEnabled) {
return resolver.ResolveCast(ResolveType(castExpression.CastTo), Resolve(castExpression.Expression));
return resolver.ResolveCast(ResolveType(castExpression.Type), Resolve(castExpression.Expression));
} else {
ScanChildren(castExpression);
return null;

1
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -146,6 +146,7 @@ @@ -146,6 +146,7 @@
<Compile Include="CSharp\Formatter\DomIndentationVisitor.cs" />
<Compile Include="CSharp\Formatter\DomSpacingVisitor.cs" />
<Compile Include="CSharp\Formatter\Indent.cs" />
<Compile Include="CSharp\OutputVisitor\InsertParenthesesVisitor.cs" />
<Compile Include="CSharp\OutputVisitor\IOutputFormatter.cs" />
<Compile Include="CSharp\OutputVisitor\OutputVisitor.cs" />
<Compile Include="CSharp\OutputVisitor\TextWriterOutputFormatter.cs" />

Loading…
Cancel
Save