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

« back to all changes in this revision

Viewing changes to external/nrefactory/ICSharpCode.NRefactory.CSharp/Analysis/DefiniteAssignmentAnalysis.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
using System.Collections.Generic;
 
21
using System.Diagnostics;
 
22
using System.Linq;
 
23
using System.Threading;
 
24
 
 
25
using ICSharpCode.NRefactory.CSharp.Resolver;
 
26
using ICSharpCode.NRefactory.Semantics;
 
27
using ICSharpCode.NRefactory.TypeSystem;
 
28
using ICSharpCode.NRefactory.TypeSystem.Implementation;
 
29
using ICSharpCode.NRefactory.Utils;
 
30
 
 
31
namespace ICSharpCode.NRefactory.CSharp.Analysis
 
32
{
 
33
        /// <summary>
 
34
        /// Represents the definite assignment status of a variable at a specific location.
 
35
        /// </summary>
 
36
        public enum DefiniteAssignmentStatus
 
37
        {
 
38
                /// <summary>
 
39
                /// The variable might be assigned or unassigned.
 
40
                /// </summary>
 
41
                PotentiallyAssigned,
 
42
                /// <summary>
 
43
                /// The variable is definitely assigned.
 
44
                /// </summary>
 
45
                DefinitelyAssigned,
 
46
                /// <summary>
 
47
                /// The variable is definitely assigned iff the expression results in the value 'true'.
 
48
                /// </summary>
 
49
                AssignedAfterTrueExpression,
 
50
                /// <summary>
 
51
                /// The variable is definitely assigned iff the expression results in the value 'false'.
 
52
                /// </summary>
 
53
                AssignedAfterFalseExpression,
 
54
                /// <summary>
 
55
                /// The code is unreachable.
 
56
                /// </summary>
 
57
                CodeUnreachable
 
58
        }
 
59
        
 
60
        /// <summary>
 
61
        /// Implements the C# definite assignment analysis (C# 4.0 Spec: §5.3 Definite assignment)
 
62
        /// </summary>
 
63
        public class DefiniteAssignmentAnalysis
 
64
        {
 
65
                sealed class DefiniteAssignmentNode : ControlFlowNode
 
66
                {
 
67
                        public int Index;
 
68
                        public DefiniteAssignmentStatus NodeStatus;
 
69
                        
 
70
                        public DefiniteAssignmentNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
 
71
                                : base(previousStatement, nextStatement, type)
 
72
                        {
 
73
                        }
 
74
                }
 
75
                
 
76
                sealed class DerivedControlFlowGraphBuilder : ControlFlowGraphBuilder
 
77
                {
 
78
                        protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
 
79
                        {
 
80
                                return new DefiniteAssignmentNode(previousStatement, nextStatement, type);
 
81
                        }
 
82
                }
 
83
                
 
84
                readonly DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor();
 
85
                readonly List<DefiniteAssignmentNode> allNodes = new List<DefiniteAssignmentNode>();
 
86
                readonly Dictionary<Statement, DefiniteAssignmentNode> beginNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
 
87
                readonly Dictionary<Statement, DefiniteAssignmentNode> endNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
 
88
                readonly Dictionary<Statement, DefiniteAssignmentNode> conditionNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
 
89
                readonly CSharpAstResolver resolver;
 
90
                Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>();
 
91
                
 
92
                string variableName;
 
93
                List<IdentifierExpression> unassignedVariableUses = new List<IdentifierExpression>();
 
94
                int analyzedRangeStart, analyzedRangeEnd;
 
95
                CancellationToken analysisCancellationToken;
 
96
                
 
97
                Queue<DefiniteAssignmentNode> nodesWithModifiedInput = new Queue<DefiniteAssignmentNode>();
 
98
                
 
99
                public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken)
 
100
                        : this(rootStatement,
 
101
                               new CSharpAstResolver(new CSharpResolver(MinimalCorlib.Instance.CreateCompilation()), rootStatement),
 
102
                               cancellationToken)
 
103
                {
 
104
                }
 
105
                
 
106
                public DefiniteAssignmentAnalysis(Statement rootStatement, CSharpAstResolver resolver, CancellationToken cancellationToken)
 
107
                {
 
108
                        if (rootStatement == null)
 
109
                                throw new ArgumentNullException("rootStatement");
 
110
                        if (resolver == null)
 
111
                                throw new ArgumentNullException("resolver");
 
112
                        this.resolver = resolver;
 
113
                        
 
114
                        visitor.analysis = this;
 
115
                        DerivedControlFlowGraphBuilder cfgBuilder = new DerivedControlFlowGraphBuilder();
 
116
                        if (resolver.TypeResolveContext.Compilation.MainAssembly.UnresolvedAssembly is MinimalCorlib) {
 
117
                                cfgBuilder.EvaluateOnlyPrimitiveConstants = true;
 
118
                        }
 
119
                        allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolver, cancellationToken).Cast<DefiniteAssignmentNode>());
 
120
                        for (int i = 0; i < allNodes.Count; i++) {
 
121
                                DefiniteAssignmentNode node = allNodes[i];
 
122
                                node.Index = i; // assign numbers to the nodes
 
123
                                if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) {
 
124
                                        // Anonymous methods have separate control flow graphs, but we also need to analyze those.
 
125
                                        // Iterate backwards so that anonymous methods are inserted in the correct order
 
126
                                        for (AstNode child = node.NextStatement.LastChild; child != null; child = child.PrevSibling) {
 
127
                                                InsertAnonymousMethods(i + 1, child, cfgBuilder, cancellationToken);
 
128
                                        }
 
129
                                }
 
130
                                // Now register the node in the dictionaries:
 
131
                                if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements)
 
132
                                        beginNodeDict.Add(node.NextStatement, node);
 
133
                                if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode)
 
134
                                        endNodeDict.Add(node.PreviousStatement, node);
 
135
                                if (node.Type == ControlFlowNodeType.LoopCondition)
 
136
                                        conditionNodeDict.Add(node.NextStatement, node);
 
137
                        }
 
138
                        // Verify that we created nodes for all statements:
 
139
                        Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
 
140
                        // Verify that we put all nodes into the dictionaries:
 
141
                        Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().All(stmt => beginNodeDict.ContainsKey(stmt)));
 
142
                        Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().All(stmt => endNodeDict.ContainsKey(stmt)));
 
143
                        
 
144
                        this.analyzedRangeStart = 0;
 
145
                        this.analyzedRangeEnd = allNodes.Count - 1;
 
146
                }
 
147
                
 
148
                void InsertAnonymousMethods(int insertPos, AstNode node, ControlFlowGraphBuilder cfgBuilder, CancellationToken cancellationToken)
 
149
                {
 
150
                        // Ignore any statements, as those have their own ControlFlowNode and get handled separately
 
151
                        if (node is Statement)
 
152
                                return;
 
153
                        AnonymousMethodExpression ame = node as AnonymousMethodExpression;
 
154
                        if (ame != null) {
 
155
                                allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph(ame.Body, resolver, cancellationToken).Cast<DefiniteAssignmentNode>());
 
156
                                return;
 
157
                        }
 
158
                        LambdaExpression lambda = node as LambdaExpression;
 
159
                        if (lambda != null && lambda.Body is Statement) {
 
160
                                allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolver, cancellationToken).Cast<DefiniteAssignmentNode>());
 
161
                                return;
 
162
                        }
 
163
                        // Descend into child expressions
 
164
                        // Iterate backwards so that anonymous methods are inserted in the correct order
 
165
                        for (AstNode child = node.LastChild; child != null; child = child.PrevSibling) {
 
166
                                InsertAnonymousMethods(insertPos, child, cfgBuilder, cancellationToken);
 
167
                        }
 
168
                }
 
169
                
 
170
                /// <summary>
 
171
                /// Gets the unassigned usages of the previously analyzed variable.
 
172
                /// </summary>
 
173
                public IList<IdentifierExpression> UnassignedVariableUses {
 
174
                        get {
 
175
                                return unassignedVariableUses.AsReadOnly();
 
176
                        }
 
177
                }
 
178
                
 
179
                /// <summary>
 
180
                /// Sets the range of statements to be analyzed.
 
181
                /// This method can be used to restrict the analysis to only a part of the method.
 
182
                /// Only the control flow paths that are fully contained within the selected part will be analyzed.
 
183
                /// </summary>
 
184
                /// <remarks>By default, both 'start' and 'end' are inclusive.</remarks>
 
185
                public void SetAnalyzedRange(Statement start, Statement end, bool startInclusive = true, bool endInclusive = true)
 
186
                {
 
187
                        var dictForStart = startInclusive ? beginNodeDict : endNodeDict;
 
188
                        var dictForEnd = endInclusive ? endNodeDict : beginNodeDict;
 
189
                        Debug.Assert(dictForStart.ContainsKey(start) && dictForEnd.ContainsKey(end));
 
190
                        int startIndex = dictForStart[start].Index;
 
191
                        int endIndex = dictForEnd[end].Index;
 
192
                        if (startIndex > endIndex)
 
193
                                throw new ArgumentException("The start statement must be lexically preceding the end statement");
 
194
                        this.analyzedRangeStart = startIndex;
 
195
                        this.analyzedRangeEnd = endIndex;
 
196
                }
 
197
                
 
198
                public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned, CancellationToken cancellationToken = default(CancellationToken))
 
199
                {
 
200
                        this.analysisCancellationToken = cancellationToken;
 
201
                        this.variableName = variable;
 
202
                        try {
 
203
                                // Reset the status:
 
204
                                unassignedVariableUses.Clear();
 
205
                                foreach (DefiniteAssignmentNode node in allNodes) {
 
206
                                        node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable;
 
207
                                        foreach (ControlFlowEdge edge in node.Outgoing)
 
208
                                                edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable;
 
209
                                }
 
210
                                
 
211
                                ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus);
 
212
                                // Iterate as long as the input status of some nodes is changing:
 
213
                                while (nodesWithModifiedInput.Count > 0) {
 
214
                                        DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue();
 
215
                                        DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable;
 
216
                                        foreach (ControlFlowEdge edge in node.Incoming) {
 
217
                                                inputStatus = MergeStatus(inputStatus, edgeStatus[edge]);
 
218
                                        }
 
219
                                        ChangeNodeStatus(node, inputStatus);
 
220
                                }
 
221
                        } finally {
 
222
                                this.analysisCancellationToken = CancellationToken.None;
 
223
                                this.variableName = null;
 
224
                        }
 
225
                }
 
226
                
 
227
                public DefiniteAssignmentStatus GetStatusBefore(Statement statement)
 
228
                {
 
229
                        return beginNodeDict[statement].NodeStatus;
 
230
                }
 
231
                
 
232
                public DefiniteAssignmentStatus GetStatusAfter(Statement statement)
 
233
                {
 
234
                        return endNodeDict[statement].NodeStatus;
 
235
                }
 
236
                
 
237
                public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement)
 
238
                {
 
239
                        return conditionNodeDict[statement].NodeStatus;
 
240
                }
 
241
                
 
242
                /// <summary>
 
243
                /// Exports the CFG. This method is intended to help debugging issues related to definite assignment.
 
244
                /// </summary>
 
245
                public GraphVizGraph ExportGraph()
 
246
                {
 
247
                        GraphVizGraph g = new GraphVizGraph();
 
248
                        g.Title = "DefiniteAssignment - " + variableName;
 
249
                        for (int i = 0; i < allNodes.Count; i++) {
 
250
                                string name = "#" + i + " = " + allNodes[i].NodeStatus.ToString() + Environment.NewLine;
 
251
                                switch (allNodes[i].Type) {
 
252
                                        case ControlFlowNodeType.StartNode:
 
253
                                        case ControlFlowNodeType.BetweenStatements:
 
254
                                                name += allNodes[i].NextStatement.ToString();
 
255
                                                break;
 
256
                                        case ControlFlowNodeType.EndNode:
 
257
                                                name += "End of " + allNodes[i].PreviousStatement.ToString();
 
258
                                                break;
 
259
                                        case ControlFlowNodeType.LoopCondition:
 
260
                                                name += "Condition in " + allNodes[i].NextStatement.ToString();
 
261
                                                break;
 
262
                                        default:
 
263
                                                name += allNodes[i].Type.ToString();
 
264
                                                break;
 
265
                                }
 
266
                                g.AddNode(new GraphVizNode(i) { label = name });
 
267
                                foreach (ControlFlowEdge edge in allNodes[i].Outgoing) {
 
268
                                        GraphVizEdge ge = new GraphVizEdge(i, ((DefiniteAssignmentNode)edge.To).Index);
 
269
                                        if (edgeStatus.Count > 0)
 
270
                                                ge.label = edgeStatus[edge].ToString();
 
271
                                        if (edge.IsLeavingTryFinally)
 
272
                                                ge.style = "dashed";
 
273
                                        switch (edge.Type) {
 
274
                                                case ControlFlowEdgeType.ConditionTrue:
 
275
                                                        ge.color = "green";
 
276
                                                        break;
 
277
                                                case ControlFlowEdgeType.ConditionFalse:
 
278
                                                        ge.color = "red";
 
279
                                                        break;
 
280
                                                case ControlFlowEdgeType.Jump:
 
281
                                                        ge.color = "blue";
 
282
                                                        break;
 
283
                                        }
 
284
                                        g.AddEdge(ge);
 
285
                                }
 
286
                        }
 
287
                        return g;
 
288
                }
 
289
                
 
290
                static DefiniteAssignmentStatus MergeStatus(DefiniteAssignmentStatus a, DefiniteAssignmentStatus b)
 
291
                {
 
292
                        // The result will be DefinitelyAssigned if at least one incoming edge is DefinitelyAssigned and all others are unreachable.
 
293
                        // The result will be DefinitelyUnassigned if at least one incoming edge is DefinitelyUnassigned and all others are unreachable.
 
294
                        // The result will be Unreachable if all incoming edges are unreachable.
 
295
                        // Otherwise, the result will be PotentiallyAssigned.
 
296
                        
 
297
                        if (a == b)
 
298
                                return a;
 
299
                        else if (a == DefiniteAssignmentStatus.CodeUnreachable)
 
300
                                return b;
 
301
                        else if (b == DefiniteAssignmentStatus.CodeUnreachable)
 
302
                                return a;
 
303
                        else
 
304
                                return DefiniteAssignmentStatus.PotentiallyAssigned;
 
305
                }
 
306
                
 
307
                void ChangeNodeStatus (DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus)
 
308
                {
 
309
                        if (node.NodeStatus == inputStatus)
 
310
                                return;
 
311
                        node.NodeStatus = inputStatus;
 
312
                        DefiniteAssignmentStatus outputStatus;
 
313
                        switch (node.Type) {
 
314
                        case ControlFlowNodeType.StartNode:
 
315
                        case ControlFlowNodeType.BetweenStatements:
 
316
                                if (node.NextStatement is IfElseStatement) {
 
317
                                        // Handle if-else as a condition node
 
318
                                                goto case ControlFlowNodeType.LoopCondition;
 
319
                                }
 
320
                                if (inputStatus == DefiniteAssignmentStatus.DefinitelyAssigned) {
 
321
                                        // There isn't any way to un-assign variables, so we don't have to check the expression
 
322
                                        // if the status already is definitely assigned.
 
323
                                        outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned;
 
324
                                } else {
 
325
                                        outputStatus = CleanSpecialValues (node.NextStatement.AcceptVisitor (visitor, inputStatus));
 
326
                                }
 
327
                                break;
 
328
                        case ControlFlowNodeType.EndNode:
 
329
                                outputStatus = inputStatus;
 
330
                                if (node.PreviousStatement.Role == TryCatchStatement.FinallyBlockRole
 
331
                                        && (outputStatus == DefiniteAssignmentStatus.DefinitelyAssigned || outputStatus == DefiniteAssignmentStatus.PotentiallyAssigned)) {
 
332
                                        TryCatchStatement tryFinally = (TryCatchStatement)node.PreviousStatement.Parent;
 
333
                                        // Changing the status on a finally block potentially changes the status of all edges leaving that finally block:
 
334
                                        foreach (ControlFlowEdge edge in allNodes.SelectMany(n => n.Outgoing)) {
 
335
                                                if (edge.IsLeavingTryFinally && edge.TryFinallyStatements.Contains (tryFinally)) {
 
336
                                                        DefiniteAssignmentStatus s = edgeStatus [edge];
 
337
                                                        if (s == DefiniteAssignmentStatus.PotentiallyAssigned) {
 
338
                                                                ChangeEdgeStatus (edge, outputStatus);
 
339
                                                        }
 
340
                                                }
 
341
                                        }
 
342
                                }
 
343
                                break;
 
344
                        case ControlFlowNodeType.LoopCondition:
 
345
                                ForeachStatement foreachStmt = node.NextStatement as ForeachStatement;
 
346
                                if (foreachStmt != null) {
 
347
                                        outputStatus = CleanSpecialValues (foreachStmt.InExpression.AcceptVisitor (visitor, inputStatus));
 
348
                                        if (foreachStmt.VariableName == this.variableName)
 
349
                                                outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned;
 
350
                                        break;
 
351
                                } else {
 
352
                                        Debug.Assert (node.NextStatement is IfElseStatement || node.NextStatement is WhileStatement || node.NextStatement is ForStatement || node.NextStatement is DoWhileStatement);
 
353
                                        Expression condition = node.NextStatement.GetChildByRole (Roles.Condition);
 
354
                                                if (condition.IsNull)
 
355
                                                        outputStatus = inputStatus;
 
356
                                                else
 
357
                                                        outputStatus = condition.AcceptVisitor(visitor, inputStatus);
 
358
                                                foreach (ControlFlowEdge edge in node.Outgoing) {
 
359
                                                        if (edge.Type == ControlFlowEdgeType.ConditionTrue && outputStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) {
 
360
                                                                ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned);
 
361
                                                        } else if (edge.Type == ControlFlowEdgeType.ConditionFalse && outputStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression) {
 
362
                                                                ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned);
 
363
                                                        } else {
 
364
                                                                ChangeEdgeStatus(edge, CleanSpecialValues(outputStatus));
 
365
                                                        }
 
366
                                                }
 
367
                                                return;
 
368
                                        }
 
369
                                default:
 
370
                                        throw new InvalidOperationException();
 
371
                        }
 
372
                        foreach (ControlFlowEdge edge in node.Outgoing) {
 
373
                                ChangeEdgeStatus(edge, outputStatus);
 
374
                        }
 
375
                }
 
376
                
 
377
                void ChangeEdgeStatus(ControlFlowEdge edge, DefiniteAssignmentStatus newStatus)
 
378
                {
 
379
                        DefiniteAssignmentStatus oldStatus = edgeStatus[edge];
 
380
                        if (oldStatus == newStatus)
 
381
                                return;
 
382
                        // Ensure that status can cannot change back to CodeUnreachable after it once was reachable.
 
383
                        // Also, don't ever use AssignedAfter... for statements.
 
384
                        if (newStatus == DefiniteAssignmentStatus.CodeUnreachable
 
385
                            || newStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression
 
386
                            || newStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
 
387
                        {
 
388
                                throw new InvalidOperationException();
 
389
                        }
 
390
                        // Note that the status can change from DefinitelyAssigned
 
391
                        // back to PotentiallyAssigned as unreachable input edges are
 
392
                        // discovered to be reachable.
 
393
                        
 
394
                        edgeStatus[edge] = newStatus;
 
395
                        DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To;
 
396
                        if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd) {
 
397
                                // TODO: potential optimization: visit previously unreachable nodes with higher priority
 
398
                                // (e.g. use Deque and enqueue previously unreachable nodes at the front, but
 
399
                                // other nodes at the end)
 
400
                                nodesWithModifiedInput.Enqueue(targetNode);
 
401
                        }
 
402
                }
 
403
                
 
404
                /// <summary>
 
405
                /// Evaluates an expression.
 
406
                /// </summary>
 
407
                /// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
 
408
                ResolveResult EvaluateConstant(Expression expr)
 
409
                {
 
410
                        return resolver.Resolve(expr, analysisCancellationToken);
 
411
                }
 
412
                
 
413
                /// <summary>
 
414
                /// Evaluates an expression.
 
415
                /// </summary>
 
416
                /// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
 
417
                bool? EvaluateCondition(Expression expr)
 
418
                {
 
419
                        ResolveResult rr = EvaluateConstant(expr);
 
420
                        if (rr != null && rr.IsCompileTimeConstant)
 
421
                                return rr.ConstantValue as bool?;
 
422
                        else
 
423
                                return null;
 
424
                }
 
425
                
 
426
                static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status)
 
427
                {
 
428
                        if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
 
429
                                return DefiniteAssignmentStatus.PotentiallyAssigned;
 
430
                        else if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
 
431
                                return DefiniteAssignmentStatus.PotentiallyAssigned;
 
432
                        else
 
433
                                return status;
 
434
                }
 
435
                
 
436
                sealed class DefiniteAssignmentVisitor : DepthFirstAstVisitor<DefiniteAssignmentStatus, DefiniteAssignmentStatus>
 
437
                {
 
438
                        internal DefiniteAssignmentAnalysis analysis;
 
439
                        
 
440
                        // The general approach for unknown nodes is to pass the status through all child nodes in order
 
441
                        protected override DefiniteAssignmentStatus VisitChildren(AstNode node, DefiniteAssignmentStatus data)
 
442
                        {
 
443
                                // the special values are valid as output only, not as input
 
444
                                Debug.Assert(data == CleanSpecialValues(data));
 
445
                                DefiniteAssignmentStatus status = data;
 
446
                                for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
 
447
                                        analysis.analysisCancellationToken.ThrowIfCancellationRequested();
 
448
                                        
 
449
                                        Debug.Assert(!(child is Statement)); // statements are visited with the CFG, not with the visitor pattern
 
450
                                        status = child.AcceptVisitor(this, status);
 
451
                                        status = CleanSpecialValues(status);
 
452
                                }
 
453
                                return status;
 
454
                        }
 
455
                        
 
456
                        #region Statements
 
457
                        // For statements, the visitor only describes the effect of the statement itself;
 
458
                        // we do not consider the effect of any nested statements.
 
459
                        // This is done because the nested statements will be reached using the control flow graph.
 
460
                        
 
461
                        // In fact, these methods are present so that the default logic in VisitChildren does not try to visit the nested statements.
 
462
                        
 
463
                        public override DefiniteAssignmentStatus VisitBlockStatement(BlockStatement blockStatement, DefiniteAssignmentStatus data)
 
464
                        {
 
465
                                return data;
 
466
                        }
 
467
                        
 
468
                        public override DefiniteAssignmentStatus VisitCheckedStatement(CheckedStatement checkedStatement, DefiniteAssignmentStatus data)
 
469
                        {
 
470
                                return data;
 
471
                        }
 
472
                        
 
473
                        public override DefiniteAssignmentStatus VisitUncheckedStatement(UncheckedStatement uncheckedStatement, DefiniteAssignmentStatus data)
 
474
                        {
 
475
                                return data;
 
476
                        }
 
477
                        
 
478
                        // ExpressionStatement handled by default logic
 
479
                        // VariableDeclarationStatement handled by default logic
 
480
                        
 
481
                        public override DefiniteAssignmentStatus VisitVariableInitializer(VariableInitializer variableInitializer, DefiniteAssignmentStatus data)
 
482
                        {
 
483
                                if (variableInitializer.Initializer.IsNull) {
 
484
                                        return data;
 
485
                                } else {
 
486
                                        DefiniteAssignmentStatus status = variableInitializer.Initializer.AcceptVisitor(this, data);
 
487
                                        if (variableInitializer.Name == analysis.variableName)
 
488
                                                return DefiniteAssignmentStatus.DefinitelyAssigned;
 
489
                                        else
 
490
                                                return status;
 
491
                                }
 
492
                        }
 
493
                        
 
494
                        // IfStatement not handled by visitor, but special-cased in the code consuming the control flow graph
 
495
                        
 
496
                        public override DefiniteAssignmentStatus VisitSwitchStatement(SwitchStatement switchStatement, DefiniteAssignmentStatus data)
 
497
                        {
 
498
                                return switchStatement.Expression.AcceptVisitor(this, data);
 
499
                        }
 
500
                        
 
501
                        public override DefiniteAssignmentStatus VisitWhileStatement(WhileStatement whileStatement, DefiniteAssignmentStatus data)
 
502
                        {
 
503
                                return data; // condition is handled by special condition CFG node
 
504
                        }
 
505
                        
 
506
                        public override DefiniteAssignmentStatus VisitDoWhileStatement(DoWhileStatement doWhileStatement, DefiniteAssignmentStatus data)
 
507
                        {
 
508
                                return data; // condition is handled by special condition CFG node
 
509
                        }
 
510
                        
 
511
                        public override DefiniteAssignmentStatus VisitForStatement(ForStatement forStatement, DefiniteAssignmentStatus data)
 
512
                        {
 
513
                                return data; // condition is handled by special condition CFG node; initializer and iterator statements are handled by CFG
 
514
                        }
 
515
                        
 
516
                        // Break/Continue/Goto: handled by default logic
 
517
                        
 
518
                        // ThrowStatement: handled by default logic (just visit the expression)
 
519
                        // ReturnStatement: handled by default logic (just visit the expression)
 
520
                        
 
521
                        public override DefiniteAssignmentStatus VisitTryCatchStatement(TryCatchStatement tryCatchStatement, DefiniteAssignmentStatus data)
 
522
                        {
 
523
                                return data; // no special logic when entering the try-catch-finally statement
 
524
                                // TODO: where to put the special logic when exiting the try-finally statement?
 
525
                        }
 
526
                        
 
527
                        public override DefiniteAssignmentStatus VisitForeachStatement(ForeachStatement foreachStatement, DefiniteAssignmentStatus data)
 
528
                        {
 
529
                                return data; // assignment of the foreach loop variable is done when handling the condition node
 
530
                        }
 
531
                        
 
532
                        public override DefiniteAssignmentStatus VisitUsingStatement(UsingStatement usingStatement, DefiniteAssignmentStatus data)
 
533
                        {
 
534
                                if (usingStatement.ResourceAcquisition is Expression)
 
535
                                        return usingStatement.ResourceAcquisition.AcceptVisitor(this, data);
 
536
                                else
 
537
                                        return data; // don't handle resource acquisition statements, as those are connected in the control flow graph
 
538
                        }
 
539
                        
 
540
                        public override DefiniteAssignmentStatus VisitLockStatement(LockStatement lockStatement, DefiniteAssignmentStatus data)
 
541
                        {
 
542
                                return lockStatement.Expression.AcceptVisitor(this, data);
 
543
                        }
 
544
                        
 
545
                        // Yield statements use the default logic
 
546
                        
 
547
                        public override DefiniteAssignmentStatus VisitUnsafeStatement(UnsafeStatement unsafeStatement, DefiniteAssignmentStatus data)
 
548
                        {
 
549
                                return data;
 
550
                        }
 
551
                        
 
552
                        public override DefiniteAssignmentStatus VisitFixedStatement(FixedStatement fixedStatement, DefiniteAssignmentStatus data)
 
553
                        {
 
554
                                DefiniteAssignmentStatus status = data;
 
555
                                foreach (var variable in fixedStatement.Variables)
 
556
                                        status = variable.AcceptVisitor(this, status);
 
557
                                return status;
 
558
                        }
 
559
                        #endregion
 
560
                        
 
561
                        #region Expressions
 
562
                        public override DefiniteAssignmentStatus VisitDirectionExpression(DirectionExpression directionExpression, DefiniteAssignmentStatus data)
 
563
                        {
 
564
                                if (directionExpression.FieldDirection == FieldDirection.Out) {
 
565
                                        return HandleAssignment(directionExpression.Expression, null, data);
 
566
                                } else {
 
567
                                        // use default logic for 'ref'
 
568
                                        return VisitChildren(directionExpression, data);
 
569
                                }
 
570
                        }
 
571
                        
 
572
                        public override DefiniteAssignmentStatus VisitAssignmentExpression(AssignmentExpression assignmentExpression, DefiniteAssignmentStatus data)
 
573
                        {
 
574
                                if (assignmentExpression.Operator == AssignmentOperatorType.Assign) {
 
575
                                        return HandleAssignment(assignmentExpression.Left, assignmentExpression.Right, data);
 
576
                                } else {
 
577
                                        // use default logic for compound assignment operators
 
578
                                        return VisitChildren(assignmentExpression, data);
 
579
                                }
 
580
                        }
 
581
                        
 
582
                        DefiniteAssignmentStatus HandleAssignment(Expression left, Expression right, DefiniteAssignmentStatus initialStatus)
 
583
                        {
 
584
                                IdentifierExpression ident = left as IdentifierExpression;
 
585
                                if (ident != null && ident.Identifier == analysis.variableName) {
 
586
                                        // right==null is special case when handling 'out' expressions
 
587
                                        if (right != null)
 
588
                                                right.AcceptVisitor(this, initialStatus);
 
589
                                        return DefiniteAssignmentStatus.DefinitelyAssigned;
 
590
                                } else {
 
591
                                        DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus);
 
592
                                        if (right != null)
 
593
                                                status = right.AcceptVisitor(this, CleanSpecialValues(status));
 
594
                                        return CleanSpecialValues(status);
 
595
                                }
 
596
                        }
 
597
                        
 
598
                        public override DefiniteAssignmentStatus VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, DefiniteAssignmentStatus data)
 
599
                        {
 
600
                                // Don't use the default logic here because we don't want to clean up the special values.
 
601
                                return parenthesizedExpression.Expression.AcceptVisitor(this, data);
 
602
                        }
 
603
                        
 
604
                        public override DefiniteAssignmentStatus VisitCheckedExpression(CheckedExpression checkedExpression, DefiniteAssignmentStatus data)
 
605
                        {
 
606
                                return checkedExpression.Expression.AcceptVisitor(this, data);
 
607
                        }
 
608
                        
 
609
                        public override DefiniteAssignmentStatus VisitUncheckedExpression(UncheckedExpression uncheckedExpression, DefiniteAssignmentStatus data)
 
610
                        {
 
611
                                return uncheckedExpression.Expression.AcceptVisitor(this, data);
 
612
                        }
 
613
                        
 
614
                        public override DefiniteAssignmentStatus VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, DefiniteAssignmentStatus data)
 
615
                        {
 
616
                                if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalAnd) {
 
617
                                        // Handle constant left side of && expressions (not in the C# spec, but done by the MS compiler)
 
618
                                        bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left);
 
619
                                        if (cond == true)
 
620
                                                return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally
 
621
                                        else if (cond == false)
 
622
                                                return data; // right operand never gets evaluated
 
623
                                        // C# 4.0 spec: §5.3.3.24 Definite Assignment for && expressions
 
624
                                        DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data);
 
625
                                        DefiniteAssignmentStatus beforeRight;
 
626
                                        if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
 
627
                                                beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned;
 
628
                                        else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
 
629
                                                beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned;
 
630
                                        else
 
631
                                                beforeRight = afterLeft;
 
632
                                        DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight);
 
633
                                        if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned)
 
634
                                                return DefiniteAssignmentStatus.DefinitelyAssigned;
 
635
                                        else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
 
636
                                                return DefiniteAssignmentStatus.DefinitelyAssigned;
 
637
                                        else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
 
638
                                                return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
 
639
                                        else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
 
640
                                                return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
 
641
                                        else
 
642
                                                return DefiniteAssignmentStatus.PotentiallyAssigned;
 
643
                                } else if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalOr) {
 
644
                                        // C# 4.0 spec: §5.3.3.25 Definite Assignment for || expressions
 
645
                                        bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left);
 
646
                                        if (cond == false)
 
647
                                                return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally
 
648
                                        else if (cond == true)
 
649
                                                return data; // right operand never gets evaluated
 
650
                                        DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data);
 
651
                                        DefiniteAssignmentStatus beforeRight;
 
652
                                        if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
 
653
                                                beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned;
 
654
                                        else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
 
655
                                                beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned;
 
656
                                        else
 
657
                                                beforeRight = afterLeft;
 
658
                                        DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight);
 
659
                                        if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned)
 
660
                                                return DefiniteAssignmentStatus.DefinitelyAssigned;
 
661
                                        else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
 
662
                                                return DefiniteAssignmentStatus.DefinitelyAssigned;
 
663
                                        else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
 
664
                                                return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
 
665
                                        else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
 
666
                                                return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
 
667
                                        else
 
668
                                                return DefiniteAssignmentStatus.PotentiallyAssigned;
 
669
                                } else if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) {
 
670
                                        // C# 4.0 spec: §5.3.3.27 Definite assignment for ?? expressions
 
671
                                        ResolveResult crr = analysis.EvaluateConstant(binaryOperatorExpression.Left);
 
672
                                        if (crr != null && crr.IsCompileTimeConstant && crr.ConstantValue == null)
 
673
                                                return binaryOperatorExpression.Right.AcceptVisitor(this, data);
 
674
                                        DefiniteAssignmentStatus status = CleanSpecialValues(binaryOperatorExpression.Left.AcceptVisitor(this, data));
 
675
                                        binaryOperatorExpression.Right.AcceptVisitor(this, status);
 
676
                                        return status;
 
677
                                } else {
 
678
                                        // use default logic for other operators
 
679
                                        return VisitChildren(binaryOperatorExpression, data);
 
680
                                }
 
681
                        }
 
682
                        
 
683
                        public override DefiniteAssignmentStatus VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, DefiniteAssignmentStatus data)
 
684
                        {
 
685
                                if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) {
 
686
                                        // C# 4.0 spec: §5.3.3.26 Definite assignment for ! expressions
 
687
                                        DefiniteAssignmentStatus status = unaryOperatorExpression.Expression.AcceptVisitor(this, data);
 
688
                                        if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
 
689
                                                return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
 
690
                                        else if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
 
691
                                                return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
 
692
                                        else
 
693
                                                return status;
 
694
                                } else {
 
695
                                        // use default logic for other operators
 
696
                                        return VisitChildren(unaryOperatorExpression, data);
 
697
                                }
 
698
                        }
 
699
                        
 
700
                        public override DefiniteAssignmentStatus VisitConditionalExpression(ConditionalExpression conditionalExpression, DefiniteAssignmentStatus data)
 
701
                        {
 
702
                                // C# 4.0 spec: §5.3.3.28 Definite assignment for ?: expressions
 
703
                                bool? cond = analysis.EvaluateCondition(conditionalExpression.Condition);
 
704
                                if (cond == true) {
 
705
                                        return conditionalExpression.TrueExpression.AcceptVisitor(this, data);
 
706
                                } else if (cond == false) {
 
707
                                        return conditionalExpression.FalseExpression.AcceptVisitor(this, data);
 
708
                                } else {
 
709
                                        DefiniteAssignmentStatus afterCondition = conditionalExpression.Condition.AcceptVisitor(this, data);
 
710
                                        
 
711
                                        DefiniteAssignmentStatus beforeTrue, beforeFalse;
 
712
                                        if (afterCondition == DefiniteAssignmentStatus.AssignedAfterTrueExpression) {
 
713
                                                beforeTrue = DefiniteAssignmentStatus.DefinitelyAssigned;
 
714
                                                beforeFalse = DefiniteAssignmentStatus.PotentiallyAssigned;
 
715
                                        } else if (afterCondition == DefiniteAssignmentStatus.AssignedAfterFalseExpression) {
 
716
                                                beforeTrue = DefiniteAssignmentStatus.PotentiallyAssigned;
 
717
                                                beforeFalse = DefiniteAssignmentStatus.DefinitelyAssigned;
 
718
                                        } else {
 
719
                                                beforeTrue = afterCondition;
 
720
                                                beforeFalse = afterCondition;
 
721
                                        }
 
722
                                        
 
723
                                        DefiniteAssignmentStatus afterTrue = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeTrue);
 
724
                                        DefiniteAssignmentStatus afterFalse = conditionalExpression.FalseExpression.AcceptVisitor(this, beforeFalse);
 
725
                                        return MergeStatus(CleanSpecialValues(afterTrue), CleanSpecialValues(afterFalse));
 
726
                                }
 
727
                        }
 
728
                        
 
729
                        public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data)
 
730
                        {
 
731
                                BlockStatement body = anonymousMethodExpression.Body;
 
732
                                analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
 
733
                                return data;
 
734
                        }
 
735
                        
 
736
                        public override DefiniteAssignmentStatus VisitLambdaExpression(LambdaExpression lambdaExpression, DefiniteAssignmentStatus data)
 
737
                        {
 
738
                                Statement body = lambdaExpression.Body as Statement;
 
739
                                if (body != null) {
 
740
                                        analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
 
741
                                } else {
 
742
                                        lambdaExpression.Body.AcceptVisitor(this, data);
 
743
                                }
 
744
                                return data;
 
745
                        }
 
746
                        
 
747
                        public override DefiniteAssignmentStatus VisitIdentifierExpression(IdentifierExpression identifierExpression, DefiniteAssignmentStatus data)
 
748
                        {
 
749
                                if (data != DefiniteAssignmentStatus.DefinitelyAssigned
 
750
                                    && identifierExpression.Identifier == analysis.variableName && identifierExpression.TypeArguments.Count == 0)
 
751
                                {
 
752
                                        analysis.unassignedVariableUses.Add(identifierExpression);
 
753
                                }
 
754
                                return data;
 
755
                        }
 
756
                        #endregion
 
757
                }
 
758
        }
 
759
}