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 | |
---|
19 | using System; |
---|
20 | |
---|
21 | namespace 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 | } |
---|