~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/nrefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertParenthesesVisitor.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}