Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/OutputVisitor/InsertParenthesesVisitor.cs @ 11937

Last change on this file since 11937 was 11700, checked in by jkarder, 9 years ago

#2077: created branch and added first version

File size: 13.9 KB
Line 
1// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20
21namespace ICSharpCode.NRefactory.CSharp
22{
23  /// <summary>
24  /// Inserts the parentheses into the AST that are needed to ensure the AST can be printed correctly.
25  /// For example, if the AST contains
26  /// BinaryOperatorExpresson(2, Mul, BinaryOperatorExpression(1, Add, 1))); printing that AST
27  /// would incorrectly result in "2 * 1 + 1". By running InsertParenthesesVisitor, the necessary
28  /// parentheses are inserted: "2 * (1 + 1)".
29  /// </summary>
30  public class InsertParenthesesVisitor : DepthFirstAstVisitor
31  {
32    /// <summary>
33    /// Gets/Sets whether the visitor should insert parentheses to make the code better looking.
34    /// If this property is false, it will insert parentheses only where strictly required by the language spec.
35    /// </summary>
36    public bool InsertParenthesesForReadability { get; set; }
37   
38    const int Primary = 16;
39    const int QueryOrLambda = 15;
40    const int Unary = 14;
41    const int RelationalAndTypeTesting = 10;
42    const int Equality = 9;
43    const int Conditional = 2;
44    const int Assignment = 1;
45   
46    /// <summary>
47    /// Gets the row number in the C# 4.0 spec operator precedence table.
48    /// </summary>
49    static int GetPrecedence(Expression expr)
50    {
51      // Note: the operator precedence table on MSDN is incorrect
52      if (expr is QueryExpression) {
53        // Not part of the table in the C# spec, but we need to ensure that queries within
54        // primary expressions get parenthesized.
55        return QueryOrLambda;
56      }
57      UnaryOperatorExpression uoe = expr as UnaryOperatorExpression;
58      if (uoe != null) {
59        if (uoe.Operator == UnaryOperatorType.PostDecrement || uoe.Operator == UnaryOperatorType.PostIncrement)
60          return Primary;
61        else
62          return Unary;
63      }
64      if (expr is CastExpression)
65        return Unary;
66      BinaryOperatorExpression boe = expr as BinaryOperatorExpression;
67      if (boe != null) {
68        switch (boe.Operator) {
69          case BinaryOperatorType.Multiply:
70          case BinaryOperatorType.Divide:
71          case BinaryOperatorType.Modulus:
72            return 13; // multiplicative
73          case BinaryOperatorType.Add:
74          case BinaryOperatorType.Subtract:
75            return 12; // additive
76          case BinaryOperatorType.ShiftLeft:
77          case BinaryOperatorType.ShiftRight:
78            return 11;
79          case BinaryOperatorType.GreaterThan:
80          case BinaryOperatorType.GreaterThanOrEqual:
81          case BinaryOperatorType.LessThan:
82          case BinaryOperatorType.LessThanOrEqual:
83            return RelationalAndTypeTesting;
84          case BinaryOperatorType.Equality:
85          case BinaryOperatorType.InEquality:
86            return Equality;
87          case BinaryOperatorType.BitwiseAnd:
88            return 8;
89          case BinaryOperatorType.ExclusiveOr:
90            return 7;
91          case BinaryOperatorType.BitwiseOr:
92            return 6;
93          case BinaryOperatorType.ConditionalAnd:
94            return 5;
95          case BinaryOperatorType.ConditionalOr:
96            return 4;
97          case BinaryOperatorType.NullCoalescing:
98            return 3;
99          default:
100            throw new NotSupportedException("Invalid value for BinaryOperatorType");
101        }
102      }
103      if (expr is IsExpression || expr is AsExpression)
104        return RelationalAndTypeTesting;
105      if (expr is ConditionalExpression)
106        return Conditional;
107      if (expr is AssignmentExpression || expr is LambdaExpression)
108        return Assignment;
109      // anything else: primary expression
110      return Primary;
111    }
112   
113    /// <summary>
114    /// Parenthesizes the expression if it does not have the minimum required precedence.
115    /// </summary>
116    static void ParenthesizeIfRequired(Expression expr, int minimumPrecedence)
117    {
118      if (GetPrecedence(expr) < minimumPrecedence) {
119        Parenthesize(expr);
120      }
121    }
122
123    static void Parenthesize(Expression expr)
124    {
125      expr.ReplaceWith(e => new ParenthesizedExpression { Expression = e });
126    }
127   
128    // Primary expressions
129    public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression)
130    {
131      ParenthesizeIfRequired(memberReferenceExpression.Target, Primary);
132      base.VisitMemberReferenceExpression(memberReferenceExpression);
133    }
134   
135    public override void VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression)
136    {
137      ParenthesizeIfRequired(pointerReferenceExpression.Target, Primary);
138      base.VisitPointerReferenceExpression(pointerReferenceExpression);
139    }
140   
141    public override void VisitInvocationExpression(InvocationExpression invocationExpression)
142    {
143      ParenthesizeIfRequired(invocationExpression.Target, Primary);
144      base.VisitInvocationExpression(invocationExpression);
145    }
146   
147    public override void VisitIndexerExpression(IndexerExpression indexerExpression)
148    {
149      ParenthesizeIfRequired(indexerExpression.Target, Primary);
150      ArrayCreateExpression ace = indexerExpression.Target as ArrayCreateExpression;
151      if (ace != null && (InsertParenthesesForReadability || ace.Initializer.IsNull)) {
152        // require parentheses for "(new int[1])[0]"
153        Parenthesize(indexerExpression.Target);
154      }
155      base.VisitIndexerExpression(indexerExpression);
156    }
157   
158    // Unary expressions
159    public override void VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression)
160    {
161      ParenthesizeIfRequired(unaryOperatorExpression.Expression, GetPrecedence(unaryOperatorExpression));
162      UnaryOperatorExpression child = unaryOperatorExpression.Expression as UnaryOperatorExpression;
163      if (child != null && InsertParenthesesForReadability)
164        Parenthesize(child);
165      base.VisitUnaryOperatorExpression(unaryOperatorExpression);
166    }
167   
168    public override void VisitCastExpression(CastExpression castExpression)
169    {
170      ParenthesizeIfRequired(castExpression.Expression, InsertParenthesesForReadability ? Primary : Unary);
171      // There's a nasty issue in the C# grammar: cast expressions including certain operators are ambiguous in some cases
172      // "(int)-1" is fine, but "(A)-b" is not a cast.
173      UnaryOperatorExpression uoe = castExpression.Expression as UnaryOperatorExpression;
174      if (uoe != null && !(uoe.Operator == UnaryOperatorType.BitNot || uoe.Operator == UnaryOperatorType.Not)) {
175        if (TypeCanBeMisinterpretedAsExpression(castExpression.Type)) {
176          Parenthesize(castExpression.Expression);
177        }
178      }
179      // The above issue can also happen with PrimitiveExpressions representing negative values:
180      PrimitiveExpression pe = castExpression.Expression as PrimitiveExpression;
181      if (pe != null && pe.Value != null && TypeCanBeMisinterpretedAsExpression(castExpression.Type)) {
182        TypeCode typeCode = Type.GetTypeCode(pe.Value.GetType());
183        switch (typeCode) {
184          case TypeCode.SByte:
185            if ((sbyte)pe.Value < 0)
186              Parenthesize(castExpression.Expression);
187            break;
188          case TypeCode.Int16:
189            if ((short)pe.Value < 0)
190              Parenthesize(castExpression.Expression);
191            break;
192          case TypeCode.Int32:
193            if ((int)pe.Value < 0)
194              Parenthesize(castExpression.Expression);
195            break;
196          case TypeCode.Int64:
197            if ((long)pe.Value < 0)
198              Parenthesize(castExpression.Expression);
199            break;
200          case TypeCode.Single:
201            if ((float)pe.Value < 0)
202              Parenthesize(castExpression.Expression);
203            break;
204          case TypeCode.Double:
205            if ((double)pe.Value < 0)
206              Parenthesize(castExpression.Expression);
207            break;
208          case TypeCode.Decimal:
209            if ((decimal)pe.Value < 0)
210              Parenthesize(castExpression.Expression);
211            break;
212        }
213      }
214      base.VisitCastExpression(castExpression);
215    }
216   
217    static bool TypeCanBeMisinterpretedAsExpression(AstType type)
218    {
219      // SimpleTypes can always be misinterpreted as IdentifierExpressions
220      // MemberTypes can be misinterpreted as MemberReferenceExpressions if they don't use double colon
221      // PrimitiveTypes or ComposedTypes can never be misinterpreted as expressions.
222      MemberType mt = type as MemberType;
223      if (mt != null)
224        return !mt.IsDoubleColon;
225      else
226        return type is SimpleType;
227    }
228   
229    // Binary Operators
230    public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression)
231    {
232      int precedence = GetPrecedence(binaryOperatorExpression);
233      if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) {
234        if (InsertParenthesesForReadability) {
235          ParenthesizeIfRequired(binaryOperatorExpression.Left, Primary);
236          ParenthesizeIfRequired(binaryOperatorExpression.Right, Primary);
237        } else {
238          // ?? is right-associative
239          ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence + 1);
240          ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence);
241        }
242      } else {
243        if (InsertParenthesesForReadability && precedence < Equality) {
244          // In readable mode, boost the priority of the left-hand side if the operator
245          // there isn't the same as the operator on this expression.
246          if (GetBinaryOperatorType(binaryOperatorExpression.Left) == binaryOperatorExpression.Operator) {
247            ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence);
248          } else {
249            ParenthesizeIfRequired(binaryOperatorExpression.Left, Equality);
250          }
251          ParenthesizeIfRequired(binaryOperatorExpression.Right, Equality);
252        } else {
253          // all other binary operators are left-associative
254          ParenthesizeIfRequired(binaryOperatorExpression.Left, precedence);
255          ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence + 1);
256        }
257      }
258      base.VisitBinaryOperatorExpression(binaryOperatorExpression);
259    }
260   
261    BinaryOperatorType? GetBinaryOperatorType(Expression expr)
262    {
263      BinaryOperatorExpression boe = expr as BinaryOperatorExpression;
264      if (boe != null)
265        return boe.Operator;
266      else
267        return null;
268    }
269   
270    public override void VisitIsExpression(IsExpression isExpression)
271    {
272      if (InsertParenthesesForReadability) {
273        // few people know the precedence of 'is', so always put parentheses in nice-looking mode.
274        ParenthesizeIfRequired(isExpression.Expression, Primary);
275      } else {
276        ParenthesizeIfRequired(isExpression.Expression, RelationalAndTypeTesting);
277      }
278      base.VisitIsExpression(isExpression);
279    }
280   
281    public override void VisitAsExpression(AsExpression asExpression)
282    {
283      if (InsertParenthesesForReadability) {
284        // few people know the precedence of 'as', so always put parentheses in nice-looking mode.
285        ParenthesizeIfRequired(asExpression.Expression, Primary);
286      } else {
287        ParenthesizeIfRequired(asExpression.Expression, RelationalAndTypeTesting);
288      }
289      base.VisitAsExpression(asExpression);
290    }
291   
292    // Conditional operator
293    public override void VisitConditionalExpression(ConditionalExpression conditionalExpression)
294    {
295      // Associativity here is a bit tricky:
296      // (a ? b : c ? d : e) == (a ? b : (c ? d : e))
297      // (a ? b ? c : d : e) == (a ? (b ? c : d) : e)
298      // Only ((a ? b : c) ? d : e) strictly needs the additional parentheses
299      if (InsertParenthesesForReadability) {
300        // Precedence of ?: can be confusing; so always put parentheses in nice-looking mode.
301        ParenthesizeIfRequired(conditionalExpression.Condition, Primary);
302        ParenthesizeIfRequired(conditionalExpression.TrueExpression, Primary);
303        ParenthesizeIfRequired(conditionalExpression.FalseExpression, Primary);
304      } else {
305        ParenthesizeIfRequired(conditionalExpression.Condition, Conditional + 1);
306        ParenthesizeIfRequired(conditionalExpression.TrueExpression, Conditional);
307        ParenthesizeIfRequired(conditionalExpression.FalseExpression, Conditional);
308      }
309      base.VisitConditionalExpression(conditionalExpression);
310    }
311   
312    public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression)
313    {
314      // assignment is right-associative
315      ParenthesizeIfRequired(assignmentExpression.Left, Assignment + 1);
316      if (InsertParenthesesForReadability) {
317        ParenthesizeIfRequired(assignmentExpression.Right, RelationalAndTypeTesting + 1);
318      } else {
319        ParenthesizeIfRequired(assignmentExpression.Right, Assignment);
320      }
321      base.VisitAssignmentExpression(assignmentExpression);
322    }
323   
324    // don't need to handle lambdas, they have lowest precedence and unambiguous associativity
325   
326    public override void VisitQueryExpression(QueryExpression queryExpression)
327    {
328      // Query expressions are strange beasts:
329      // "var a = -from b in c select d;" is valid, so queries bind stricter than unary expressions.
330      // However, the end of the query is greedy. So their start sort of has a high precedence,
331      // while their end has a very low precedence. We handle this by checking whether a query is used
332      // as left part of a binary operator, and parenthesize it if required.
333      if (queryExpression.Role == BinaryOperatorExpression.LeftRole)
334        Parenthesize(queryExpression);
335      if (queryExpression.Parent is IsExpression || queryExpression.Parent is AsExpression)
336        Parenthesize(queryExpression);
337      if (InsertParenthesesForReadability) {
338        // when readability is desired, always parenthesize query expressions within unary or binary operators
339        if (queryExpression.Parent is UnaryOperatorExpression || queryExpression.Parent is BinaryOperatorExpression)
340          Parenthesize(queryExpression);
341      }
342      base.VisitQueryExpression(queryExpression);
343    }
344  }
345}
Note: See TracBrowser for help on using the repository browser.