Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Analysis/DefiniteAssignmentAnalysis.cs @ 13371

Last change on this file since 13371 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 33.9 KB
Line 
1// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.Collections.Generic;
21using System.Diagnostics;
22using System.Linq;
23using System.Threading;
24
25using ICSharpCode.NRefactory.CSharp.Resolver;
26using ICSharpCode.NRefactory.Semantics;
27using ICSharpCode.NRefactory.TypeSystem;
28using ICSharpCode.NRefactory.TypeSystem.Implementation;
29using ICSharpCode.NRefactory.Utils;
30
31namespace 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}
Note: See TracBrowser for help on using the repository browser.