Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ALPS/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Analysis/NullValueAnalysis.cs @ 12062

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

#2077: created branch and added first version

File size: 89.5 KB
Line 
1//
2// NullValueAnalysis.cs
3// 
4// Author:
5//       Luís Reis <luiscubal@gmail.com>
6//
7// Copyright (c) 2013 Luís Reis
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13// copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25// THE SOFTWARE.
26
27using System;
28using System.Collections;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.Linq;
32using System.Threading;
33using System.Text;
34using ICSharpCode.NRefactory.CSharp.Resolver;
35using ICSharpCode.NRefactory.Semantics;
36using ICSharpCode.NRefactory.TypeSystem;
37using ICSharpCode.NRefactory.CSharp.Refactoring;
38using ICSharpCode.NRefactory.PatternMatching;
39using ICSharpCode.NRefactory.CSharp;
40using ICSharpCode.NRefactory.Utils;
41
42namespace ICSharpCode.NRefactory.CSharp.Analysis
43{
44  public class NullValueAnalysis
45  {
46    sealed class VariableStatusInfo : IEquatable<VariableStatusInfo>, IEnumerable<KeyValuePair<string, NullValueStatus>>
47    {
48      readonly Dictionary<string, NullValueStatus> VariableStatus = new Dictionary<string, NullValueStatus>();
49
50      public NullValueStatus this[string name]
51      {
52        get {
53          NullValueStatus status;
54          if (VariableStatus.TryGetValue(name, out status)) {
55            return status;
56          }
57          return NullValueStatus.UnreachableOrInexistent;
58        }
59        set {
60          if (value == NullValueStatus.UnreachableOrInexistent) {
61            VariableStatus.Remove(name);
62          } else {
63            VariableStatus [name] = value;
64          }
65        }
66      }
67
68      /// <summary>
69      /// Modifies the variable state to consider a new incoming path
70      /// </summary>
71      /// <returns><c>true</c>, if the state has changed, <c>false</c> otherwise.</returns>
72      /// <param name="incomingState">The variable state of the incoming path</param>
73      public bool ReceiveIncoming(VariableStatusInfo incomingState)
74      {
75        bool changed = false;
76        var listOfVariables = VariableStatus.Keys.Concat(incomingState.VariableStatus.Keys).ToList();
77        foreach (string variable in listOfVariables)
78        {
79          var newValue = CombineStatus(this [variable], incomingState [variable]);
80          if (this [variable] != newValue) {
81            this [variable] = newValue;
82            changed = true;
83          }
84        }
85
86        return changed;
87      }
88
89      public static NullValueStatus CombineStatus(NullValueStatus oldValue, NullValueStatus incomingValue)
90      {
91        if (oldValue == NullValueStatus.Error || incomingValue == NullValueStatus.Error)
92          return NullValueStatus.Error;
93
94        if (oldValue == NullValueStatus.UnreachableOrInexistent ||
95            oldValue == NullValueStatus.Unassigned)
96          return incomingValue;
97
98        if (incomingValue == NullValueStatus.Unassigned) {
99          return NullValueStatus.Unassigned;
100        }
101
102        if (oldValue == NullValueStatus.CapturedUnknown || incomingValue == NullValueStatus.CapturedUnknown) {
103          //TODO: Check if this is right
104          return NullValueStatus.CapturedUnknown;
105        }
106
107        if (oldValue == NullValueStatus.Unknown) {
108          return NullValueStatus.Unknown;
109        }
110
111        if (oldValue == NullValueStatus.DefinitelyNull) {
112          return incomingValue == NullValueStatus.DefinitelyNull ?
113            NullValueStatus.DefinitelyNull : NullValueStatus.PotentiallyNull;
114        }
115
116        if (oldValue == NullValueStatus.DefinitelyNotNull) {
117          if (incomingValue == NullValueStatus.Unknown)
118            return NullValueStatus.Unknown;
119          if (incomingValue == NullValueStatus.DefinitelyNotNull)
120            return NullValueStatus.DefinitelyNotNull;
121          return NullValueStatus.PotentiallyNull;
122        }
123
124        Debug.Assert(oldValue == NullValueStatus.PotentiallyNull);
125        return NullValueStatus.PotentiallyNull;
126      }
127
128      public bool HasVariable(string variable) {
129        return VariableStatus.ContainsKey(variable);
130      }
131
132      public VariableStatusInfo Clone() {
133        var clone = new VariableStatusInfo();
134        foreach (var item in VariableStatus) {
135          clone.VariableStatus.Add(item.Key, item.Value);
136        }
137        return clone;
138      }
139
140      public override bool Equals(object obj)
141      {
142        return Equals(obj as VariableStatusInfo);
143      }
144
145      public bool Equals(VariableStatusInfo obj)
146      {
147        if (obj == null) {
148          return false;
149        }
150
151        if (VariableStatus.Count != obj.VariableStatus.Count)
152          return false;
153
154        return VariableStatus.All(item => item.Value == obj[item.Key]);
155      }
156
157      public override int GetHashCode()
158      {
159        //STUB
160        return VariableStatus.Count.GetHashCode();
161      }
162
163      public static bool operator ==(VariableStatusInfo obj1, VariableStatusInfo obj2) {
164        return object.ReferenceEquals(obj1, null) ?
165          object.ReferenceEquals(obj2, null) : obj1.Equals(obj2);
166      }
167
168      public static bool operator !=(VariableStatusInfo obj1, VariableStatusInfo obj2) {
169        return !(obj1 == obj2);
170      }
171
172      public IEnumerator<KeyValuePair<string, NullValueStatus>> GetEnumerator()
173      {
174        return VariableStatus.GetEnumerator();
175      }
176
177      IEnumerator IEnumerable.GetEnumerator()
178      {
179        return GetEnumerator();
180      }
181
182      public override string ToString()
183      {
184        var builder = new StringBuilder("[");
185        foreach (var item in this) {
186          builder.Append(item.Key);
187          builder.Append("=");
188          builder.Append(item.Value);
189        }
190        builder.Append("]");
191        return builder.ToString();
192      }
193    }
194
195    sealed class NullAnalysisNode : ControlFlowNode
196    {
197      public readonly VariableStatusInfo VariableState = new VariableStatusInfo();
198      public bool Visited { get; private set; }
199
200      public NullAnalysisNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
201        : base(previousStatement, nextStatement, type)
202      {
203      }
204
205      public bool ReceiveIncoming(VariableStatusInfo incomingState)
206      {
207        bool changed = VariableState.ReceiveIncoming(incomingState);
208        if (!Visited) {
209          Visited = true;
210          return true;
211        }
212        return changed;
213      }
214    }
215
216    sealed class NullAnalysisGraphBuilder : ControlFlowGraphBuilder
217    {
218      protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
219      {
220        return new NullAnalysisNode(previousStatement, nextStatement, type);
221      }
222    }
223
224    class PendingNode : IEquatable<PendingNode> {
225      internal readonly NullAnalysisNode nodeToVisit;
226      internal readonly VariableStatusInfo statusInfo;
227      internal readonly ComparableList<NullAnalysisNode> pendingTryFinallyNodes;
228      internal readonly NullAnalysisNode nodeAfterFinally;
229
230      internal PendingNode(NullAnalysisNode nodeToVisit, VariableStatusInfo statusInfo)
231        : this(nodeToVisit, statusInfo, new ComparableList<NullAnalysisNode>(), null)
232      {
233      }
234
235      public PendingNode(NullAnalysisNode nodeToVisit, VariableStatusInfo statusInfo, ComparableList<NullAnalysisNode> pendingFinallyNodes, NullAnalysisNode nodeAfterFinally)
236      {
237        this.nodeToVisit = nodeToVisit;
238        this.statusInfo = statusInfo;
239        this.pendingTryFinallyNodes = pendingFinallyNodes;
240        this.nodeAfterFinally = nodeAfterFinally;
241      }
242
243      public override bool Equals(object obj)
244      {
245        return Equals(obj as PendingNode);
246      }
247
248      public bool Equals(PendingNode obj) {
249        if (obj == null) return false;
250
251        if (nodeToVisit != obj.nodeToVisit) return false;
252        if (statusInfo != obj.statusInfo) return false;
253        if (pendingTryFinallyNodes != obj.pendingTryFinallyNodes) return false;
254        if (nodeAfterFinally != obj.nodeAfterFinally) return false;
255
256        return true;
257      }
258
259      public override int GetHashCode()
260      {
261        return nodeToVisit.GetHashCode() ^
262          statusInfo.GetHashCode() ^
263          pendingTryFinallyNodes.GetHashCode() ^
264          (nodeAfterFinally == null ? 0 : nodeAfterFinally.GetHashCode());
265      }
266    }
267
268    readonly BaseRefactoringContext context;
269    readonly NullAnalysisVisitor visitor;
270    List<NullAnalysisNode> allNodes;
271    readonly HashSet<PendingNode> nodesToVisit = new HashSet<PendingNode>();
272    Dictionary<Statement, NullAnalysisNode> nodeBeforeStatementDict;
273    Dictionary<Statement, NullAnalysisNode> nodeAfterStatementDict;
274    readonly Dictionary<Expression, NullValueStatus> expressionResult = new Dictionary<Expression, NullValueStatus>();
275
276    public NullValueAnalysis(BaseRefactoringContext context, MethodDeclaration methodDeclaration, CancellationToken cancellationToken)
277      : this(context, methodDeclaration.Body, methodDeclaration.Parameters, cancellationToken)
278    {
279    }
280
281    readonly IEnumerable<ParameterDeclaration> parameters;
282    readonly Statement rootStatement;
283
284    readonly CancellationToken cancellationToken;
285
286    public NullValueAnalysis(BaseRefactoringContext context, Statement rootStatement, IEnumerable<ParameterDeclaration> parameters, CancellationToken cancellationToken)
287    {
288      if (rootStatement == null)
289        throw new ArgumentNullException("rootStatement");
290      if (context == null)
291        throw new ArgumentNullException("context");
292
293      this.context = context;
294      this.rootStatement = rootStatement;
295      this.parameters = parameters;
296      this.visitor = new NullAnalysisVisitor(this);
297      this.cancellationToken = cancellationToken;
298    }
299
300    /// <summary>
301    /// Sets the local variable value.
302    /// This method does not change anything if identifier does not refer to a local variable.
303    /// Do not use this in variable declarations since resolving the variable won't work yet.
304    /// </summary>
305    /// <returns><c>true</c>, if local variable value was set, <c>false</c> otherwise.</returns>
306    /// <param name="data">The variable status data to change.</param>
307    /// <param name="identifierNode">The identifier to set.</param>
308    /// <param name="identifierName">The name of the identifier to set.</param>
309    /// <param name="value">The value to set the identifier.</param>
310    bool SetLocalVariableValue (VariableStatusInfo data, AstNode identifierNode, string identifierName, NullValueStatus value) {
311      var resolveResult = context.Resolve(identifierNode);
312      if (resolveResult is LocalResolveResult) {
313        if (data [identifierName] != NullValueStatus.CapturedUnknown) {
314          data [identifierName] = value;
315
316          return true;
317        }
318      }
319      return false;
320    }
321
322    bool SetLocalVariableValue (VariableStatusInfo data, IdentifierExpression identifierExpression, NullValueStatus value) {
323      return SetLocalVariableValue(data, identifierExpression, identifierExpression.Identifier, value);
324    }
325
326    bool SetLocalVariableValue (VariableStatusInfo data, Identifier identifier, NullValueStatus value) {
327      return SetLocalVariableValue(data, identifier, identifier.Name, value);
328    }
329
330    void SetupNode(NullAnalysisNode node)
331    {
332      foreach (var parameter in parameters) {
333        var resolveResult = context.Resolve(parameter.Type);
334        node.VariableState[parameter.Name] = GetInitialVariableStatus(resolveResult);
335      }
336
337      nodesToVisit.Add(new PendingNode(node, node.VariableState));
338    }
339
340    static bool IsTypeNullable(IType type)
341    {
342      return type.IsReferenceType == true || type.FullName == "System.Nullable";
343    }
344
345    public bool IsParametersAreUninitialized {
346      get;
347      set;
348    }
349
350    NullValueStatus GetInitialVariableStatus(ResolveResult resolveResult)
351    {
352      var typeResolveResult = resolveResult as TypeResolveResult;
353      if (typeResolveResult == null) {
354        return NullValueStatus.Error;
355      }
356      var type = typeResolveResult.Type;
357      if (type.IsReferenceType == null) {
358        return NullValueStatus.Error;
359      }
360      if (!IsParametersAreUninitialized)
361        return NullValueStatus.DefinitelyNotNull;
362      return IsTypeNullable(type) ? NullValueStatus.PotentiallyNull : NullValueStatus.DefinitelyNotNull;
363    }
364
365    public void Analyze()
366    {
367      var cfgBuilder = new NullAnalysisGraphBuilder();
368      allNodes = cfgBuilder.BuildControlFlowGraph(rootStatement, cancellationToken).Cast<NullAnalysisNode>().ToList();
369      nodeBeforeStatementDict = allNodes.Where(node => node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements)
370        .ToDictionary(node => node.NextStatement);
371      nodeAfterStatementDict = allNodes.Where(node => node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode)
372        .ToDictionary(node => node.PreviousStatement);
373
374      foreach (var node in allNodes) {
375        if (node.Type == ControlFlowNodeType.StartNode && node.NextStatement == rootStatement) {
376          Debug.Assert(!nodesToVisit.Any());
377
378          SetupNode(node);
379        }
380      }
381
382      while (nodesToVisit.Any()) {
383        var nodeToVisit = nodesToVisit.First();
384        nodesToVisit.Remove(nodeToVisit);
385
386        Visit(nodeToVisit);
387      }
388    }
389
390    int visits = 0;
391
392    public int NodeVisits
393    {
394      get {
395        return visits;
396      }
397    }
398
399    void Visit(PendingNode nodeInfo)
400    {
401      cancellationToken.ThrowIfCancellationRequested();
402
403      var node = nodeInfo.nodeToVisit;
404      var statusInfo = nodeInfo.statusInfo;
405
406      visits++;
407      if (visits > 100) {
408        //Visiting way too often, let's enter fast mode
409        //Fast mode is slighly less accurate but visits each node less times
410        nodesToVisit.RemoveWhere(candidate => candidate.nodeToVisit == nodeInfo.nodeToVisit &&
411                                 candidate.pendingTryFinallyNodes.Equals(nodeInfo.pendingTryFinallyNodes) &&
412                                 candidate.nodeAfterFinally == nodeInfo.nodeAfterFinally);
413        statusInfo = node.VariableState;
414      }
415
416      var nextStatement = node.NextStatement;
417      VariableStatusInfo outgoingStatusInfo = statusInfo;
418      VisitorResult result = null;
419
420      if (nextStatement != null && (!(nextStatement is DoWhileStatement) || node.Type == ControlFlowNodeType.LoopCondition)) {
421        result = nextStatement.AcceptVisitor(visitor, statusInfo);
422        if (result == null) {
423          Console.WriteLine("Failure in {0}", nextStatement);
424          throw new InvalidOperationException();
425        }
426
427        outgoingStatusInfo = result.Variables;
428      }
429
430      if ((result == null || !result.ThrowsException) && node.Outgoing.Any()) {
431        var tryFinallyStatement = nextStatement as TryCatchStatement;
432
433        foreach (var outgoingEdge in node.Outgoing) {
434          VariableStatusInfo edgeInfo;
435          edgeInfo = outgoingStatusInfo.Clone();
436
437          if (node.Type == ControlFlowNodeType.EndNode) {
438            var previousBlock = node.PreviousStatement as BlockStatement;
439            if (previousBlock != null) {
440              //We're leaving a block statement.
441              //As such, we'll remove the variables that were declared *in* the loop
442              //This helps GetVariableStatusAfter/BeforeStatement be more accurate
443              //and prevents some redundant revisiting.
444
445              foreach (var variableInitializer in previousBlock.Statements
446                       .OfType<VariableDeclarationStatement>()
447                       .SelectMany(declaration => declaration.Variables)) {
448
449                edgeInfo [variableInitializer.Name] = NullValueStatus.UnreachableOrInexistent;
450              }
451            }
452          }
453
454          if (tryFinallyStatement != null) {
455            //With the exception of try statements, this needs special handling:
456            //we'll set all changed variables to Unknown or CapturedUnknown
457            if (outgoingEdge.To.NextStatement == tryFinallyStatement.FinallyBlock) {
458              foreach (var identifierExpression in tryFinallyStatement.TryBlock.Descendants.OfType<IdentifierExpression>()) {
459                //TODO: Investigate CaptureUnknown
460                SetLocalVariableValue(edgeInfo, identifierExpression, NullValueStatus.Unknown);
461              }
462            } else {
463              var clause = tryFinallyStatement.CatchClauses
464                .FirstOrDefault(candidateClause => candidateClause.Body == outgoingEdge.To.NextStatement);
465
466              if (clause != null) {
467                SetLocalVariableValue(edgeInfo, clause.VariableNameToken, NullValueStatus.DefinitelyNotNull);
468
469                foreach (var identifierExpression in tryFinallyStatement.TryBlock.Descendants.OfType<IdentifierExpression>()) {
470                  //TODO: Investigate CaptureUnknown
471                  SetLocalVariableValue(edgeInfo, identifierExpression, NullValueStatus.Unknown);
472                }
473              }
474            }
475          }
476
477          if (result != null) {
478            switch (outgoingEdge.Type) {
479              case ControlFlowEdgeType.ConditionTrue:
480                if (result.KnownBoolResult == false) {
481                  //No need to explore this path -- expression is known to be false
482                  continue;
483                }
484                edgeInfo = result.TruePathVariables;
485                break;
486              case ControlFlowEdgeType.ConditionFalse:
487                if (result.KnownBoolResult == true) {
488                  //No need to explore this path -- expression is known to be true
489                  continue;
490                }
491                edgeInfo = result.FalsePathVariables;
492                break;
493            }
494          }
495
496          if (outgoingEdge.IsLeavingTryFinally) {
497            var nodeAfterFinally = (NullAnalysisNode)outgoingEdge.To;
498            var finallyNodes = outgoingEdge.TryFinallyStatements.Select(tryFinally => nodeBeforeStatementDict [tryFinally.FinallyBlock]).ToList();
499            var nextNode = finallyNodes.First();
500            var remainingFinallyNodes = new ComparableList<NullAnalysisNode>(finallyNodes.Skip(1));
501            //We have to visit the node even if ReceiveIncoming returns false
502            //since the finallyNodes/nodeAfterFinally might be different even if the values of variables are the same -- and they need to be visited either way!
503            //TODO 1: Is there any point in visiting the finally statement here?
504            //TODO 2: Do we need the ReceiveIncoming at all?
505            nextNode.ReceiveIncoming(edgeInfo);
506            nodesToVisit.Add(new PendingNode(nextNode, edgeInfo, remainingFinallyNodes, nodeAfterFinally));
507          } else {
508            var outgoingNode = (NullAnalysisNode)outgoingEdge.To;
509            if (outgoingNode.ReceiveIncoming(edgeInfo)) {
510              nodesToVisit.Add(new PendingNode(outgoingNode, edgeInfo));
511            }
512          }
513        }
514      } else {
515        //We found a return/throw/yield break or some other termination node
516        var finallyBlockStarts = nodeInfo.pendingTryFinallyNodes;
517        var nodeAfterFinally = nodeInfo.nodeAfterFinally;
518
519        if (finallyBlockStarts.Any()) {
520          var nextNode = finallyBlockStarts.First();
521          if (nextNode.ReceiveIncoming(outgoingStatusInfo))
522            nodesToVisit.Add(new PendingNode(nextNode, outgoingStatusInfo, new ComparableList<NullAnalysisNode>(finallyBlockStarts.Skip(1)), nodeInfo.nodeAfterFinally));
523        } else if (nodeAfterFinally != null && nodeAfterFinally.ReceiveIncoming(outgoingStatusInfo)) {
524          nodesToVisit.Add(new PendingNode(nodeAfterFinally, outgoingStatusInfo));
525        } else {
526          //Maybe we finished a try/catch/finally statement the "normal" way (no direct jumps)
527          //so let's check that case
528          var statement = node.PreviousStatement ?? node.NextStatement;
529          Debug.Assert(statement != null);
530          var parent = statement.GetParent<Statement>();
531          var parentTryCatch = parent as TryCatchStatement;
532          if (parentTryCatch != null) {
533            var nextNode = nodeAfterStatementDict [parentTryCatch];
534            if (nextNode.ReceiveIncoming(outgoingStatusInfo)) {
535              nodesToVisit.Add(new PendingNode(nextNode, outgoingStatusInfo));
536            }
537          }
538        }
539      }
540    }
541
542    public NullValueStatus GetExpressionResult(Expression expr)
543    {
544      if (expr == null)
545        throw new ArgumentNullException("expr");
546
547      NullValueStatus info;
548      if (expressionResult.TryGetValue(expr, out info)) {
549        return info;
550      }
551
552      return NullValueStatus.UnreachableOrInexistent;
553    }
554
555    public NullValueStatus GetVariableStatusBeforeStatement(Statement stmt, string variableName)
556    {
557      if (stmt == null)
558        throw new ArgumentNullException("stmt");
559      if (variableName == null)
560        throw new ArgumentNullException("variableName");
561
562      NullAnalysisNode node;
563      if (nodeBeforeStatementDict.TryGetValue(stmt, out node)) {
564        return node.VariableState [variableName];
565      }
566
567      return NullValueStatus.UnreachableOrInexistent;
568    }
569
570    public NullValueStatus GetVariableStatusAfterStatement(Statement stmt, string variableName)
571    {
572      if (stmt == null)
573        throw new ArgumentNullException("stmt");
574      if (variableName == null)
575        throw new ArgumentNullException("variableName");
576
577      NullAnalysisNode node;
578      if (nodeAfterStatementDict.TryGetValue(stmt, out node)) {
579        return node.VariableState [variableName];
580      }
581
582      return NullValueStatus.UnreachableOrInexistent;
583    }
584
585    class ConditionalBranchInfo
586    {
587      /// <summary>
588      /// True if the variable is null for the true path, false if it is false for the true path.
589      /// </summary>
590      public Dictionary<string, bool> TrueResultVariableNullStates = new Dictionary<string, bool>();
591      /// <summary>
592      /// True if the variable is null for the false path, false if it is false for the false path.
593      /// </summary>
594      public Dictionary<string, bool> FalseResultVariableNullStates = new Dictionary<string, bool>();
595    }
596
597    class VisitorResult
598    {
599      /// <summary>
600      /// Indicates the return value of the expression.
601      /// </summary>
602      /// <remarks>
603      /// Only applicable for expressions.
604      /// </remarks>
605      public NullValueStatus NullableReturnResult;
606
607      /// <summary>
608      /// Indicates the value of each item in an array or linq query.
609      /// </summary>
610      public NullValueStatus EnumeratedValueResult;
611
612      /// <summary>
613      /// Information that indicates the restrictions to add
614      /// when branching.
615      /// </summary>
616      /// <remarks>
617      /// Used in if/else statements, conditional expressions and
618      /// while statements.
619      /// </remarks>
620      public ConditionalBranchInfo ConditionalBranchInfo;
621
622      /// <summary>
623      /// The state of the variables after the expression is executed.
624      /// </summary>
625      public VariableStatusInfo Variables;
626
627      /// <summary>
628      /// The expression is known to be invalid and trigger an error
629      /// (e.g. a NullReferenceException)
630      /// </summary>
631      public bool ThrowsException;
632
633      /// <summary>
634      /// The known bool result of an expression.
635      /// </summary>
636      public bool? KnownBoolResult;
637
638      public static VisitorResult ForEnumeratedValue(VariableStatusInfo variables, NullValueStatus itemValues)
639      {
640        var result = new VisitorResult();
641        result.NullableReturnResult = NullValueStatus.DefinitelyNotNull;
642        result.EnumeratedValueResult = itemValues;
643        result.Variables = variables.Clone();
644        return result;
645      }
646
647      public static VisitorResult ForValue(VariableStatusInfo variables, NullValueStatus returnValue)
648      {
649        var result = new VisitorResult();
650        result.NullableReturnResult = returnValue;
651        result.Variables = variables.Clone();
652        return result;
653      }
654
655      public static VisitorResult ForBoolValue(VariableStatusInfo variables, bool newValue)
656      {
657        var result = new VisitorResult();
658        result.NullableReturnResult = NullValueStatus.DefinitelyNotNull; //Bool expressions are never null
659        result.KnownBoolResult = newValue;
660        result.Variables = variables.Clone();
661        return result;
662      }
663
664      public static VisitorResult ForException(VariableStatusInfo variables) {
665        var result = new VisitorResult();
666        result.NullableReturnResult = NullValueStatus.UnreachableOrInexistent;
667        result.ThrowsException = true;
668        result.Variables = variables.Clone();
669        return result;
670      }
671
672      public VisitorResult Negated {
673        get {
674          var result = new VisitorResult();
675          if (NullableReturnResult.IsDefiniteValue()) {
676            result.NullableReturnResult = NullableReturnResult == NullValueStatus.DefinitelyNull
677              ? NullValueStatus.DefinitelyNotNull : NullValueStatus.DefinitelyNull;
678          } else {
679            result.NullableReturnResult = NullableReturnResult;
680          }
681          result.Variables = Variables.Clone();
682          result.KnownBoolResult = !KnownBoolResult;
683          if (ConditionalBranchInfo != null) {
684            result.ConditionalBranchInfo = new ConditionalBranchInfo();
685            foreach (var item in ConditionalBranchInfo.TrueResultVariableNullStates) {
686              result.ConditionalBranchInfo.FalseResultVariableNullStates [item.Key] = item.Value;
687            }
688            foreach (var item in ConditionalBranchInfo.FalseResultVariableNullStates) {
689              result.ConditionalBranchInfo.TrueResultVariableNullStates [item.Key] = item.Value;
690            }
691          }
692          return result;
693        }
694      }
695
696      public VariableStatusInfo TruePathVariables {
697        get {
698          var variables = Variables.Clone();
699          if (ConditionalBranchInfo != null) {
700            foreach (var item in ConditionalBranchInfo.TrueResultVariableNullStates) {
701              variables [item.Key] = item.Value ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull;
702            }
703          }
704          return variables;
705        }
706      }
707
708      public VariableStatusInfo FalsePathVariables {
709        get {
710          var variables = Variables.Clone();
711          if (ConditionalBranchInfo != null) {
712            foreach (var item in ConditionalBranchInfo.FalseResultVariableNullStates) {
713              variables [item.Key] = item.Value ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull;
714            }
715          }
716          return variables;
717        }
718      }
719
720      public static VisitorResult AndOperation(VisitorResult tentativeLeftResult, VisitorResult tentativeRightResult)
721      {
722        var result = new VisitorResult();
723        result.KnownBoolResult = tentativeLeftResult.KnownBoolResult & tentativeRightResult.KnownBoolResult;
724
725        var trueTruePath = tentativeRightResult.TruePathVariables;
726        var trueFalsePath = tentativeRightResult.FalsePathVariables;
727        var falsePath = tentativeLeftResult.FalsePathVariables;
728
729        var trueVariables = trueTruePath;
730
731        var falseVariables = trueFalsePath.Clone();
732        falseVariables.ReceiveIncoming(falsePath);
733        result.Variables = trueVariables.Clone();
734        result.Variables.ReceiveIncoming(falseVariables);
735
736        result.ConditionalBranchInfo = new ConditionalBranchInfo();
737
738        foreach (var variable in trueVariables) {
739          if (!variable.Value.IsDefiniteValue())
740            continue;
741
742          string variableName = variable.Key;
743
744          if (variable.Value != result.Variables[variableName]) {
745            bool isNull = variable.Value == NullValueStatus.DefinitelyNull;
746            result.ConditionalBranchInfo.TrueResultVariableNullStates.Add(variableName, isNull);
747          }
748        }
749
750        foreach (var variable in falseVariables) {
751          if (!variable.Value.IsDefiniteValue())
752            continue;
753
754          string variableName = variable.Key;
755
756          if (variable.Value != result.Variables [variableName]) {
757            bool isNull = variable.Value == NullValueStatus.DefinitelyNull;
758            result.ConditionalBranchInfo.FalseResultVariableNullStates.Add(variableName, isNull);
759          }
760        }
761
762        return result;
763      }
764
765      public static VisitorResult OrOperation(VisitorResult tentativeLeftResult, VisitorResult tentativeRightResult)
766      {
767        return VisitorResult.AndOperation(tentativeLeftResult.Negated, tentativeRightResult.Negated).Negated;
768      }
769    }
770
771    class NullAnalysisVisitor : DepthFirstAstVisitor<VariableStatusInfo, VisitorResult>
772    {
773      NullValueAnalysis analysis;
774
775      public NullAnalysisVisitor(NullValueAnalysis analysis) {
776        this.analysis = analysis;
777      }
778
779      protected override VisitorResult VisitChildren(AstNode node, VariableStatusInfo data)
780      {
781        Debug.Fail("Missing override for " + node.GetType().Name);
782        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
783      }
784     
785      public override VisitorResult VisitNullNode(AstNode nullNode, VariableStatusInfo data)
786      {
787        // can occur due to syntax errors
788        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
789      }
790     
791      public override VisitorResult VisitEmptyStatement(EmptyStatement emptyStatement, VariableStatusInfo data)
792      {
793        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
794      }
795
796      public override VisitorResult VisitBlockStatement(BlockStatement blockStatement, VariableStatusInfo data)
797      {
798        //We'll visit the child statements later (we'll visit each one directly from the CFG)
799        //As such this is mostly a dummy node.
800        return new VisitorResult { Variables = data };
801      }
802
803      public override VisitorResult VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, VariableStatusInfo data)
804      {
805        foreach (var variable in variableDeclarationStatement.Variables) {
806          var result = variable.AcceptVisitor(this, data);
807          if (result.ThrowsException)
808            return result;
809          data = result.Variables;
810        }
811
812        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
813      }
814
815      public override VisitorResult VisitVariableInitializer(VariableInitializer variableInitializer, VariableStatusInfo data)
816      {
817        if (variableInitializer.Initializer.IsNull) {
818          data = data.Clone();
819          data[variableInitializer.Name] = NullValueStatus.Unassigned;
820        } else {
821          var result = variableInitializer.Initializer.AcceptVisitor(this, data);
822          if (result.ThrowsException)
823            return result;
824          data = result.Variables.Clone();
825          data[variableInitializer.Name] = result.NullableReturnResult;
826        }
827
828        return VisitorResult.ForValue(data, data [variableInitializer.Name]);
829      }
830
831      public override VisitorResult VisitIfElseStatement(IfElseStatement ifElseStatement, VariableStatusInfo data)
832      {
833        //We'll visit the true/false statements later (directly from the CFG)
834        return ifElseStatement.Condition.AcceptVisitor(this, data);
835      }
836
837      public override VisitorResult VisitWhileStatement(WhileStatement whileStatement, VariableStatusInfo data)
838      {
839        return whileStatement.Condition.AcceptVisitor(this, data);
840      }
841
842      public override VisitorResult VisitDoWhileStatement(DoWhileStatement doWhileStatement, VariableStatusInfo data)
843      {
844        return doWhileStatement.Condition.AcceptVisitor(this, data);
845      }
846
847      public override VisitorResult VisitForStatement(ForStatement forStatement, VariableStatusInfo data)
848      {
849        //The initializers, the embedded statement and the iterators aren't visited here
850        //because they have their own CFG nodes.
851        if (forStatement.Condition.IsNull)
852          return VisitorResult.ForValue(data, NullValueStatus.Unknown);
853        return forStatement.Condition.AcceptVisitor(this, data);
854      }
855
856      public override VisitorResult VisitForeachStatement(ForeachStatement foreachStatement, VariableStatusInfo data)
857      {
858        var newVariable = foreachStatement.VariableNameToken;
859        var inExpressionResult = foreachStatement.InExpression.AcceptVisitor(this, data);
860        if (inExpressionResult.ThrowsException)
861          return inExpressionResult;
862
863        var newData = inExpressionResult.Variables.Clone();
864
865        var resolveResult = analysis.context.Resolve(foreachStatement.VariableNameToken) as LocalResolveResult;
866        if (resolveResult != null) {
867          //C# 5.0 changed the meaning of foreach so that each iteration declares a new variable
868          //as such, the variable is "uncaptured" only for C# >= 5.0
869          if (analysis.context.Supports(new Version(5, 0)) || data[newVariable.Name] != NullValueStatus.CapturedUnknown) {
870            newData[newVariable.Name] = NullValueAnalysis.IsTypeNullable(resolveResult.Type) ? inExpressionResult.EnumeratedValueResult : NullValueStatus.DefinitelyNotNull;
871          }
872        }
873
874        return VisitorResult.ForValue(newData, NullValueStatus.Unknown);
875      }
876
877      public override VisitorResult VisitUsingStatement(UsingStatement usingStatement, VariableStatusInfo data)
878      {
879        return usingStatement.ResourceAcquisition.AcceptVisitor(this, data);
880      }
881
882      public override VisitorResult VisitFixedStatement(FixedStatement fixedStatement, VariableStatusInfo data)
883      {
884        foreach (var variable in fixedStatement.Variables) {
885          var result = variable.AcceptVisitor(this, data);
886          if (result.ThrowsException)
887            return result;
888          data = result.Variables;
889        }
890
891        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
892      }
893
894      public override VisitorResult VisitSwitchStatement(SwitchStatement switchStatement, VariableStatusInfo data)
895      {
896        //We could do better than this, but it would require special handling outside the visitor
897        //so for now, for simplicity, we'll just take the easy way
898
899        var tentativeResult = switchStatement.Expression.AcceptVisitor(this, data);
900        if (tentativeResult.ThrowsException) {
901          return tentativeResult;
902        }
903
904        foreach (var section in switchStatement.SwitchSections) {
905          //No need to check for ThrowsException, since it will always be false (see VisitSwitchSection)
906          section.AcceptVisitor(this, tentativeResult.Variables);
907        }
908
909        return VisitorResult.ForValue(tentativeResult.Variables, NullValueStatus.Unknown);
910      }
911
912      public override VisitorResult VisitSwitchSection(SwitchSection switchSection, VariableStatusInfo data)
913      {
914        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
915      }
916
917      public override VisitorResult VisitExpressionStatement(ExpressionStatement expressionStatement, VariableStatusInfo data)
918      {
919        return expressionStatement.Expression.AcceptVisitor(this, data);
920      }
921
922      public override VisitorResult VisitReturnStatement(ReturnStatement returnStatement, VariableStatusInfo data)
923      {
924        if (returnStatement.Expression.IsNull)
925          return VisitorResult.ForValue(data, NullValueStatus.Unknown);
926        return returnStatement.Expression.AcceptVisitor(this, data);
927      }
928
929      public override VisitorResult VisitTryCatchStatement(TryCatchStatement tryCatchStatement, VariableStatusInfo data)
930      {
931        //The needs special treatment in the analyser itself
932        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
933      }
934
935      public override VisitorResult VisitBreakStatement(BreakStatement breakStatement, VariableStatusInfo data)
936      {
937        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
938      }
939
940      public override VisitorResult VisitContinueStatement(ContinueStatement continueStatement, VariableStatusInfo data)
941      {
942        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
943      }
944
945      public override VisitorResult VisitGotoStatement(GotoStatement gotoStatement, VariableStatusInfo data)
946      {
947        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
948      }
949
950      public override VisitorResult VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, VariableStatusInfo data)
951      {
952        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
953      }
954
955      public override VisitorResult VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, VariableStatusInfo data)
956      {
957        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
958      }
959
960      public override VisitorResult VisitLabelStatement(LabelStatement labelStatement, VariableStatusInfo data)
961      {
962        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
963      }
964
965      public override VisitorResult VisitUnsafeStatement(UnsafeStatement unsafeStatement, VariableStatusInfo data)
966      {
967        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
968      }
969
970      public override VisitorResult VisitLockStatement(LockStatement lockStatement, VariableStatusInfo data)
971      {
972        var expressionResult = lockStatement.Expression.AcceptVisitor(this, data);
973        if (expressionResult.ThrowsException)
974          return expressionResult;
975
976        if (expressionResult.NullableReturnResult == NullValueStatus.DefinitelyNull) {
977          return VisitorResult.ForException(expressionResult.Variables);
978        }
979
980        var identifier = CSharpUtil.GetInnerMostExpression(lockStatement.Expression) as IdentifierExpression;
981        if (identifier != null) {
982          var identifierValue = expressionResult.Variables [identifier.Identifier];
983          if (identifierValue != NullValueStatus.CapturedUnknown) {
984            var newVariables = expressionResult.Variables.Clone();
985            analysis.SetLocalVariableValue(newVariables, identifier, NullValueStatus.DefinitelyNotNull);
986
987            return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown);
988          }
989        }
990
991        return VisitorResult.ForValue(expressionResult.Variables, NullValueStatus.Unknown);
992      }
993
994      public override VisitorResult VisitThrowStatement(ThrowStatement throwStatement, VariableStatusInfo data)
995      {
996        if (throwStatement.Expression.IsNull)
997          return VisitorResult.ForValue(data, NullValueStatus.DefinitelyNotNull);
998        return throwStatement.Expression.AcceptVisitor(this, data);
999      }
1000
1001      public override VisitorResult VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, VariableStatusInfo data)
1002      {
1003        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
1004      }
1005
1006      public override VisitorResult VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, VariableStatusInfo data)
1007      {
1008        return yieldReturnStatement.Expression.AcceptVisitor(this, data);
1009      }
1010
1011      public override VisitorResult VisitCheckedStatement(CheckedStatement checkedStatement, VariableStatusInfo data)
1012      {
1013        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
1014      }
1015
1016      public override VisitorResult VisitUncheckedStatement(UncheckedStatement uncheckedStatement, VariableStatusInfo data)
1017      {
1018        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
1019      }
1020
1021      void RegisterExpressionResult(Expression expression, NullValueStatus expressionResult)
1022      {
1023        NullValueStatus oldStatus;
1024        if (analysis.expressionResult.TryGetValue(expression, out oldStatus)) {
1025          analysis.expressionResult[expression] = VariableStatusInfo.CombineStatus(analysis.expressionResult[expression], expressionResult);
1026        }
1027        else {
1028          analysis.expressionResult[expression] = expressionResult;
1029        }
1030      }
1031
1032      VisitorResult HandleExpressionResult(Expression expression, VariableStatusInfo dataAfterExpression, NullValueStatus expressionResult) {
1033        RegisterExpressionResult(expression, expressionResult);
1034
1035        return VisitorResult.ForValue(dataAfterExpression, expressionResult);
1036      }
1037
1038      VisitorResult HandleExpressionResult(Expression expression, VariableStatusInfo dataAfterExpression, bool expressionResult) {
1039        RegisterExpressionResult(expression, NullValueStatus.DefinitelyNotNull);
1040
1041        return VisitorResult.ForBoolValue(dataAfterExpression, expressionResult);
1042      }
1043
1044      VisitorResult HandleExpressionResult(Expression expression, VisitorResult result) {
1045        RegisterExpressionResult(expression, result.NullableReturnResult);
1046
1047        return result;
1048      }
1049
1050      public override VisitorResult VisitAssignmentExpression(AssignmentExpression assignmentExpression, VariableStatusInfo data)
1051      {
1052        var tentativeResult = assignmentExpression.Left.AcceptVisitor(this, data);
1053        if (tentativeResult.ThrowsException)
1054          return HandleExpressionResult(assignmentExpression, tentativeResult);
1055        tentativeResult = assignmentExpression.Right.AcceptVisitor(this, tentativeResult.Variables);
1056        if (tentativeResult.ThrowsException)
1057          return HandleExpressionResult(assignmentExpression, tentativeResult);
1058
1059        var leftIdentifier = assignmentExpression.Left as IdentifierExpression;
1060        if (leftIdentifier != null) {
1061          var resolveResult = analysis.context.Resolve(leftIdentifier);
1062          if (resolveResult.IsError) {
1063            return HandleExpressionResult(assignmentExpression, data, NullValueStatus.Error);
1064          }
1065
1066          if (resolveResult is LocalResolveResult) {
1067            var result = new VisitorResult();
1068            result.NullableReturnResult = tentativeResult.NullableReturnResult;
1069            result.Variables = tentativeResult.Variables.Clone();
1070            var oldValue = result.Variables [leftIdentifier.Identifier];
1071
1072            if (assignmentExpression.Operator == AssignmentOperatorType.Assign ||
1073                oldValue == NullValueStatus.Unassigned ||
1074                oldValue == NullValueStatus.DefinitelyNotNull ||
1075                tentativeResult.NullableReturnResult == NullValueStatus.Error ||
1076                tentativeResult.NullableReturnResult == NullValueStatus.Unknown) {
1077              analysis.SetLocalVariableValue(result.Variables, leftIdentifier, tentativeResult.NullableReturnResult);
1078            } else {
1079              if (oldValue == NullValueStatus.DefinitelyNull) {
1080                //Do nothing --it'll remain null
1081              } else {
1082                analysis.SetLocalVariableValue(result.Variables, leftIdentifier, NullValueStatus.PotentiallyNull);
1083              }
1084            }
1085
1086            return HandleExpressionResult(assignmentExpression, result);
1087          }
1088        }
1089
1090        return HandleExpressionResult(assignmentExpression, tentativeResult);
1091      }
1092
1093      public override VisitorResult VisitIdentifierExpression(IdentifierExpression identifierExpression, VariableStatusInfo data)
1094      {
1095        var resolveResult = analysis.context.Resolve(identifierExpression);
1096        if (resolveResult.IsError) {
1097          return HandleExpressionResult(identifierExpression, data, NullValueStatus.Error);
1098        }
1099        var local = resolveResult as LocalResolveResult;
1100        if (local != null) {
1101          var value = data [local.Variable.Name];
1102          if (value == NullValueStatus.CapturedUnknown)
1103            value = NullValueStatus.Unknown;
1104          return HandleExpressionResult(identifierExpression, data, value);
1105        }
1106        if (resolveResult.IsCompileTimeConstant) {
1107          object value = resolveResult.ConstantValue;
1108          if (value == null) {
1109            return HandleExpressionResult(identifierExpression, data, NullValueStatus.DefinitelyNull);
1110          }
1111          var boolValue = value as bool?;
1112          if (boolValue != null) {
1113            return VisitorResult.ForBoolValue(data, (bool)boolValue);
1114          }
1115          return HandleExpressionResult(identifierExpression, data, NullValueStatus.DefinitelyNotNull);
1116        }
1117
1118        var memberResolveResult = resolveResult as MemberResolveResult;
1119
1120        var returnValue = GetFieldReturnValue(memberResolveResult, data);
1121
1122        return HandleExpressionResult(identifierExpression, data, returnValue);
1123      }
1124
1125      public override VisitorResult VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, VariableStatusInfo data)
1126      {
1127        var resolveResult = analysis.context.Resolve(defaultValueExpression);
1128        if (resolveResult.IsError) {
1129          return HandleExpressionResult(defaultValueExpression, data, NullValueStatus.Unknown);
1130        }
1131
1132        Debug.Assert(resolveResult.IsCompileTimeConstant);
1133
1134        var status = resolveResult.ConstantValue == null  && resolveResult.Type.IsReferenceType != false ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull;
1135        return HandleExpressionResult(defaultValueExpression, data, status);
1136      }
1137
1138      public override VisitorResult VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, VariableStatusInfo data)
1139      {
1140        return HandleExpressionResult(nullReferenceExpression, data, NullValueStatus.DefinitelyNull);
1141      }
1142
1143      public override VisitorResult VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, VariableStatusInfo data)
1144      {
1145        return HandleExpressionResult(primitiveExpression, data, NullValueStatus.DefinitelyNotNull);
1146      }
1147
1148      public override VisitorResult VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, VariableStatusInfo data)
1149      {
1150        return HandleExpressionResult(parenthesizedExpression, parenthesizedExpression.Expression.AcceptVisitor(this, data));
1151      }
1152
1153      public override VisitorResult VisitConditionalExpression(ConditionalExpression conditionalExpression, VariableStatusInfo data)
1154      {
1155        var tentativeBaseResult = conditionalExpression.Condition.AcceptVisitor(this, data);
1156        if (tentativeBaseResult.ThrowsException)
1157          return HandleExpressionResult(conditionalExpression, tentativeBaseResult);
1158
1159        var conditionResolveResult = analysis.context.Resolve(conditionalExpression.Condition);
1160
1161        if (tentativeBaseResult.KnownBoolResult == true || true.Equals(conditionResolveResult.ConstantValue)) {
1162          return HandleExpressionResult(conditionalExpression, conditionalExpression.TrueExpression.AcceptVisitor(this, tentativeBaseResult.TruePathVariables));
1163        }
1164        if (tentativeBaseResult.KnownBoolResult == false || false.Equals(conditionResolveResult.ConstantValue)) {
1165          return HandleExpressionResult(conditionalExpression, conditionalExpression.FalseExpression.AcceptVisitor(this, tentativeBaseResult.FalsePathVariables));
1166        }
1167
1168        //No known bool result
1169        var trueCaseResult = conditionalExpression.TrueExpression.AcceptVisitor(this, tentativeBaseResult.TruePathVariables);
1170        if (trueCaseResult.ThrowsException) {
1171          //We know that the true case will never be completed, then the right case is the only possible route.
1172          return HandleExpressionResult(conditionalExpression, conditionalExpression.FalseExpression.AcceptVisitor(this, tentativeBaseResult.FalsePathVariables));
1173        }
1174        var falseCaseResult = conditionalExpression.FalseExpression.AcceptVisitor(this, tentativeBaseResult.FalsePathVariables);
1175        if (falseCaseResult.ThrowsException) {
1176          return HandleExpressionResult(conditionalExpression, trueCaseResult.Variables, true);
1177        }
1178
1179        return HandleExpressionResult(conditionalExpression, VisitorResult.OrOperation(trueCaseResult, falseCaseResult));
1180      }
1181
1182      public override VisitorResult VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data)
1183      {
1184        //Let's not evaluate the sides just yet because of ??, && and ||
1185
1186        //We'll register the results here (with HandleExpressionResult)
1187        //so each Visit*Expression won't have to do it itself
1188        switch (binaryOperatorExpression.Operator) {
1189          case BinaryOperatorType.ConditionalAnd:
1190            return HandleExpressionResult(binaryOperatorExpression, VisitConditionalAndExpression(binaryOperatorExpression, data));
1191          case BinaryOperatorType.ConditionalOr:
1192            return HandleExpressionResult(binaryOperatorExpression, VisitConditionalOrExpression(binaryOperatorExpression, data));
1193          case BinaryOperatorType.NullCoalescing:
1194            return HandleExpressionResult(binaryOperatorExpression, VisitNullCoalescing(binaryOperatorExpression, data));
1195          case BinaryOperatorType.Equality:
1196            return HandleExpressionResult(binaryOperatorExpression, VisitEquality(binaryOperatorExpression, data));
1197          case BinaryOperatorType.InEquality:
1198            return HandleExpressionResult(binaryOperatorExpression, VisitEquality(binaryOperatorExpression, data).Negated);
1199          default:
1200            return HandleExpressionResult(binaryOperatorExpression, VisitOtherBinaryExpression(binaryOperatorExpression, data));
1201        }
1202      }
1203
1204      VisitorResult VisitOtherBinaryExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data)
1205      {
1206        var leftTentativeResult = binaryOperatorExpression.Left.AcceptVisitor(this, data);
1207        if (leftTentativeResult.ThrowsException)
1208          return leftTentativeResult;
1209        var rightTentativeResult = binaryOperatorExpression.Right.AcceptVisitor(this, leftTentativeResult.Variables);
1210        if (rightTentativeResult.ThrowsException)
1211          return rightTentativeResult;
1212
1213        //TODO: Assuming operators are not overloaded by users
1214        // (or, if they are, that they retain similar behavior to the default ones)
1215
1216        switch (binaryOperatorExpression.Operator) {
1217          case BinaryOperatorType.LessThan:
1218          case BinaryOperatorType.GreaterThan:
1219            //Operations < and > with nulls always return false
1220            //Those same operations will other values may or may not return false
1221            if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull &&
1222              rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) {
1223              return VisitorResult.ForBoolValue(rightTentativeResult.Variables, false);
1224            }
1225            //We don't know what the value is, but we know that both true and false are != null.
1226            return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNotNull);
1227          case BinaryOperatorType.LessThanOrEqual:
1228          case BinaryOperatorType.GreaterThanOrEqual:
1229            if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) {
1230              if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull)
1231                return VisitorResult.ForBoolValue(rightTentativeResult.Variables, true);
1232              if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull)
1233                return VisitorResult.ForBoolValue(rightTentativeResult.Variables, false);
1234            } else if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) {
1235              if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull)
1236                return VisitorResult.ForBoolValue(rightTentativeResult.Variables, false);
1237            }
1238
1239            return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.Unknown);
1240          default:
1241            //Anything else: null + anything == anything + null == null.
1242            //not null + not null = not null
1243            if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) {
1244              return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNull);
1245            }
1246            if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull) {
1247              if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull)
1248                return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNull);
1249              if (rightTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull)
1250                return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.DefinitelyNotNull);
1251            }
1252
1253            return VisitorResult.ForValue(rightTentativeResult.Variables, NullValueStatus.Unknown);
1254        }
1255      }
1256
1257      VisitorResult WithVariableValue(VisitorResult result, IdentifierExpression identifier, bool isNull)
1258      {
1259        var localVariableResult = analysis.context.Resolve(identifier) as LocalResolveResult;
1260        if (localVariableResult != null) {
1261          result.ConditionalBranchInfo.TrueResultVariableNullStates[identifier.Identifier] = isNull;
1262          if (isNull) {
1263            result.ConditionalBranchInfo.FalseResultVariableNullStates[identifier.Identifier] = false;
1264          }
1265        }
1266        return result;
1267      }
1268
1269      VisitorResult VisitEquality(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data)
1270      {
1271        //TODO: Should this check for user operators?
1272
1273        var tentativeLeftResult = binaryOperatorExpression.Left.AcceptVisitor(this, data);
1274        if (tentativeLeftResult.ThrowsException)
1275          return tentativeLeftResult;
1276        var tentativeRightResult = binaryOperatorExpression.Right.AcceptVisitor(this, tentativeLeftResult.Variables);
1277        if (tentativeRightResult.ThrowsException)
1278          return tentativeRightResult;
1279
1280        if (tentativeLeftResult.KnownBoolResult != null && tentativeLeftResult.KnownBoolResult == tentativeRightResult.KnownBoolResult) {
1281          return VisitorResult.ForBoolValue(tentativeRightResult.Variables, true);
1282        }
1283
1284        if (tentativeLeftResult.KnownBoolResult != null && tentativeLeftResult.KnownBoolResult == !tentativeRightResult.KnownBoolResult) {
1285          return VisitorResult.ForBoolValue(tentativeRightResult.Variables, false);
1286        }
1287
1288        if (tentativeLeftResult.NullableReturnResult.IsDefiniteValue()) {
1289          if (tentativeRightResult.NullableReturnResult.IsDefiniteValue()) {
1290            if (tentativeLeftResult.NullableReturnResult == NullValueStatus.DefinitelyNull || tentativeRightResult.NullableReturnResult == NullValueStatus.DefinitelyNull) {
1291              return VisitorResult.ForBoolValue(tentativeRightResult.Variables, tentativeLeftResult.NullableReturnResult == tentativeRightResult.NullableReturnResult);
1292            }
1293          }
1294        }
1295
1296        var result = new VisitorResult();
1297        result.Variables = tentativeRightResult.Variables;
1298        result.NullableReturnResult = NullValueStatus.Unknown;
1299        result.ConditionalBranchInfo = new ConditionalBranchInfo();
1300
1301        if (tentativeRightResult.NullableReturnResult.IsDefiniteValue()) {
1302          var identifier = CSharpUtil.GetInnerMostExpression(binaryOperatorExpression.Left) as IdentifierExpression;
1303
1304          if (identifier != null) {
1305            bool isNull = (tentativeRightResult.NullableReturnResult == NullValueStatus.DefinitelyNull);
1306
1307            WithVariableValue(result, identifier, isNull);
1308          }
1309        }
1310
1311        if (tentativeLeftResult.NullableReturnResult.IsDefiniteValue()) {
1312          var identifier = CSharpUtil.GetInnerMostExpression(binaryOperatorExpression.Right) as IdentifierExpression;
1313
1314          if (identifier != null) {
1315            bool isNull = (tentativeLeftResult.NullableReturnResult == NullValueStatus.DefinitelyNull);
1316
1317            WithVariableValue(result, identifier, isNull);
1318          }
1319        }
1320
1321        return result;
1322      }
1323
1324      VisitorResult VisitConditionalAndExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data)
1325      {
1326        var tentativeLeftResult = binaryOperatorExpression.Left.AcceptVisitor(this, data);
1327        if (tentativeLeftResult.KnownBoolResult == false || tentativeLeftResult.ThrowsException) {
1328          return tentativeLeftResult;
1329        }
1330
1331        var truePath = tentativeLeftResult.TruePathVariables;
1332        var tentativeRightResult = binaryOperatorExpression.Right.AcceptVisitor(this, truePath);
1333        if (tentativeRightResult.ThrowsException) {
1334          //If the true path throws an exception, then the only way for the expression to complete
1335          //successfully is if the left expression is false
1336          return VisitorResult.ForBoolValue(tentativeLeftResult.FalsePathVariables, false);
1337        }
1338
1339        return VisitorResult.AndOperation(tentativeLeftResult, tentativeRightResult);
1340      }
1341
1342      VisitorResult VisitConditionalOrExpression(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data)
1343      {
1344        var tentativeLeftResult = binaryOperatorExpression.Left.AcceptVisitor(this, data);
1345        if (tentativeLeftResult.KnownBoolResult == true || tentativeLeftResult.ThrowsException) {
1346          return tentativeLeftResult;
1347        }
1348
1349        var falsePath = tentativeLeftResult.FalsePathVariables;
1350        var tentativeRightResult = binaryOperatorExpression.Right.AcceptVisitor(this, falsePath);
1351        if (tentativeRightResult.ThrowsException) {
1352          //If the false path throws an exception, then the only way for the expression to complete
1353          //successfully is if the left expression is true
1354          return VisitorResult.ForBoolValue(tentativeLeftResult.TruePathVariables, true);
1355        }
1356
1357        return VisitorResult.OrOperation(tentativeLeftResult, tentativeRightResult);
1358      }
1359
1360      VisitorResult VisitNullCoalescing(BinaryOperatorExpression binaryOperatorExpression, VariableStatusInfo data)
1361      {
1362        var leftTentativeResult = binaryOperatorExpression.Left.AcceptVisitor(this, data);
1363        if (leftTentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNotNull || leftTentativeResult.ThrowsException) {
1364          return leftTentativeResult;
1365        }
1366
1367        //If the right side is found, then the left side is known to be null
1368        var newData = leftTentativeResult.Variables;
1369        var leftIdentifier = CSharpUtil.GetInnerMostExpression(binaryOperatorExpression.Left) as IdentifierExpression;
1370        if (leftIdentifier != null) {
1371          newData = newData.Clone();
1372          analysis.SetLocalVariableValue(newData, leftIdentifier, NullValueStatus.DefinitelyNull);
1373        }
1374
1375        var rightTentativeResult = binaryOperatorExpression.Right.AcceptVisitor(this, newData);
1376        if (rightTentativeResult.ThrowsException) {
1377          //This means the left expression was not null all along (or else the expression will throw an exception)
1378
1379          if (leftIdentifier != null) {
1380            newData = newData.Clone();
1381            analysis.SetLocalVariableValue(newData, leftIdentifier, NullValueStatus.DefinitelyNotNull);
1382            return VisitorResult.ForValue(newData, NullValueStatus.DefinitelyNotNull);
1383          }
1384
1385          return VisitorResult.ForValue(leftTentativeResult.Variables, NullValueStatus.DefinitelyNotNull);
1386        }
1387
1388        var mergedVariables = rightTentativeResult.Variables;
1389        var nullValue = rightTentativeResult.NullableReturnResult;
1390
1391        if (leftTentativeResult.NullableReturnResult != NullValueStatus.DefinitelyNull) {
1392          mergedVariables = mergedVariables.Clone();
1393          mergedVariables.ReceiveIncoming(leftTentativeResult.Variables);
1394          if (nullValue == NullValueStatus.DefinitelyNull) {
1395            nullValue = NullValueStatus.PotentiallyNull;
1396          }
1397        }
1398
1399        return VisitorResult.ForValue(mergedVariables, nullValue);
1400      }
1401
1402      public override VisitorResult VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, VariableStatusInfo data)
1403      {
1404        //TODO: Again, what to do when overloaded operators are found?
1405
1406        var tentativeResult = unaryOperatorExpression.Expression.AcceptVisitor(this, data);
1407        if (tentativeResult.ThrowsException)
1408          return HandleExpressionResult(unaryOperatorExpression, tentativeResult);
1409
1410        if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) {
1411          return HandleExpressionResult(unaryOperatorExpression, tentativeResult.Negated);
1412        }
1413        return HandleExpressionResult(unaryOperatorExpression, tentativeResult);
1414      }
1415
1416      public override VisitorResult VisitInvocationExpression(InvocationExpression invocationExpression, VariableStatusInfo data)
1417      {
1418        //TODO: Handle some common methods such as string.IsNullOrEmpty
1419
1420        var targetResult = invocationExpression.Target.AcceptVisitor(this, data);
1421        if (targetResult.ThrowsException)
1422          return HandleExpressionResult(invocationExpression, targetResult);
1423
1424        data = targetResult.Variables;
1425
1426        var methodResolveResult = analysis.context.Resolve(invocationExpression) as CSharpInvocationResolveResult;
1427
1428        List<VisitorResult> parameterResults = new List<VisitorResult>();
1429
1430        foreach (var argumentToHandle in invocationExpression.Arguments.Select((argument, parameterIndex) => new { argument, parameterIndex })) {
1431          var argument = argumentToHandle.argument;
1432          var parameterIndex = argumentToHandle.parameterIndex;
1433
1434          var result = argument.AcceptVisitor(this, data);
1435          if (result.ThrowsException)
1436            return HandleExpressionResult(invocationExpression, result);
1437          parameterResults.Add(result);
1438
1439          var namedArgument = argument as NamedArgumentExpression;
1440         
1441          var directionExpression = (namedArgument == null ? argument : namedArgument.Expression) as DirectionExpression;
1442          if (directionExpression != null && methodResolveResult != null) {
1443            var identifier = directionExpression.Expression as IdentifierExpression;
1444            if (identifier != null) {
1445              //out and ref parameters do *NOT* capture the variable (since they must stop changing it by the time they return)
1446              var identifierResolveResult = analysis.context.Resolve(identifier) as LocalResolveResult;
1447              if (identifierResolveResult != null && IsTypeNullable(identifierResolveResult.Type)) {
1448                data = data.Clone();
1449
1450                FixParameter(argument, methodResolveResult.Member.Parameters, parameterIndex, identifier, data);
1451              }
1452            }
1453
1454
1455            continue;
1456          }
1457
1458          data = result.Variables;
1459        }
1460
1461        var identifierExpression = CSharpUtil.GetInnerMostExpression(invocationExpression.Target) as IdentifierExpression;
1462        if (identifierExpression != null) {
1463          if (targetResult.NullableReturnResult == NullValueStatus.DefinitelyNull) {
1464            return HandleExpressionResult(invocationExpression, VisitorResult.ForException(data));
1465          }
1466
1467          var descendentIdentifiers = invocationExpression.Arguments.SelectMany(argument => argument.DescendantsAndSelf).OfType<IdentifierExpression>();
1468          if (!descendentIdentifiers.Any(identifier => identifier.Identifier == identifierExpression.Identifier)) {
1469            //TODO: We can make this check better (see VisitIndexerExpression for more details)
1470            data = data.Clone();
1471            analysis.SetLocalVariableValue(data, identifierExpression, NullValueStatus.DefinitelyNotNull);
1472          }
1473        }
1474
1475        return HandleExpressionResult(invocationExpression, GetMethodVisitorResult(methodResolveResult, data, parameterResults));
1476      }
1477
1478      static VisitorResult GetMethodVisitorResult(CSharpInvocationResolveResult methodResolveResult, VariableStatusInfo data, List<VisitorResult> parameterResults)
1479      {
1480        if (methodResolveResult == null)
1481          return VisitorResult.ForValue(data, NullValueStatus.Unknown);
1482
1483        var method = methodResolveResult.Member as IMethod;
1484        if (method != null) {
1485          if (method.GetAttribute(new FullTypeName(AnnotationNames.AssertionMethodAttribute)) != null) {
1486            var assertionParameters = method.Parameters.Select((parameter, index) => new { index, parameter })
1487              .Select(parameter => new { parameter.index, parameter.parameter, attributes = parameter.parameter.Attributes.Where(attribute => attribute.AttributeType.FullName == AnnotationNames.AssertionConditionAttribute).ToList() })
1488              .Where(parameter => parameter.attributes.Count() == 1)
1489              .Select(parameter => new { parameter.index, parameter.parameter, attribute = parameter.attributes[0] })
1490              .ToList();
1491
1492            //Unclear what should be done if there are multiple assertion conditions
1493            if (assertionParameters.Count() == 1) {
1494              Debug.Assert(methodResolveResult.Arguments.Count == parameterResults.Count);
1495
1496              var assertionParameter = assertionParameters [0];
1497              VisitorResult assertionParameterResult = null;
1498
1499              object intendedResult = true;
1500              var positionalArgument = assertionParameter.attribute.PositionalArguments.FirstOrDefault() as MemberResolveResult;
1501              if (positionalArgument != null && positionalArgument.Type.FullName == AnnotationNames.AssertionConditionTypeAttribute) {
1502                switch (positionalArgument.Member.FullName) {
1503                  case AnnotationNames.AssertionConditionTypeIsTrue:
1504                    intendedResult = true;
1505                    break;
1506                  case AnnotationNames.AssertionConditionTypeIsFalse:
1507                    intendedResult = false;
1508                    break;
1509                  case AnnotationNames.AssertionConditionTypeIsNull:
1510                    intendedResult = null;
1511                    break;
1512                  case AnnotationNames.AssertionConditionTypeIsNotNull:
1513                    intendedResult = "<not-null>";
1514                    break;
1515                }
1516              }
1517
1518              int parameterIndex = assertionParameter.index;
1519              if (assertionParameter.index < methodResolveResult.Arguments.Count && !(methodResolveResult.Arguments [assertionParameter.index] is NamedArgumentResolveResult)) {
1520                //Use index
1521                assertionParameterResult = parameterResults [assertionParameter.index];
1522              } else {
1523                //Use named argument
1524                int? nameIndex = methodResolveResult.Arguments.Select((argument, index) => new { argument, index})
1525                  .Where(argument => {
1526                    var namedArgument = argument.argument as NamedArgumentResolveResult;
1527                    return namedArgument != null && namedArgument.ParameterName == assertionParameter.parameter.Name;
1528                  }).Select(argument => (int?)argument.index).FirstOrDefault();
1529
1530                if (nameIndex != null) {
1531                  parameterIndex = nameIndex.Value;
1532                  assertionParameterResult = parameterResults [nameIndex.Value];
1533                } else if (assertionParameter.parameter.IsOptional) {
1534                  //Try to use default value
1535
1536                  if (intendedResult is string) {
1537                    if (assertionParameter.parameter.ConstantValue == null) {
1538                      return VisitorResult.ForException(data);
1539                    }
1540                  } else {
1541                    if (!object.Equals(assertionParameter.parameter.ConstantValue, intendedResult)) {
1542                      return VisitorResult.ForException(data);
1543                    }
1544                  }
1545                } else {
1546                  //The parameter was not specified, yet it is not optional?
1547                  return VisitorResult.ForException(data);
1548                }
1549              }
1550
1551              //Now check assertion
1552              if (assertionParameterResult != null) {
1553                if (intendedResult is bool) {
1554                  if (assertionParameterResult.KnownBoolResult == !(bool)intendedResult) {
1555                    return VisitorResult.ForException(data);
1556                  }
1557
1558                  data = (bool)intendedResult ? assertionParameterResult.TruePathVariables : assertionParameterResult.FalsePathVariables;
1559                } else {
1560                  bool shouldBeNull = intendedResult == null;
1561
1562                  if (assertionParameterResult.NullableReturnResult == (shouldBeNull ? NullValueStatus.DefinitelyNotNull : NullValueStatus.DefinitelyNull)) {
1563                    return VisitorResult.ForException(data);
1564                  }
1565
1566                  var parameterResolveResult = methodResolveResult.Arguments [parameterIndex];
1567
1568                  LocalResolveResult localVariableResult = null;
1569
1570                  var conversionResolveResult = parameterResolveResult as ConversionResolveResult;
1571                  if (conversionResolveResult != null) {
1572                    if (!IsTypeNullable(conversionResolveResult.Type)) {
1573                      if (intendedResult == null) {
1574                        return VisitorResult.ForException(data);
1575                      }
1576                    } else {
1577                      localVariableResult = conversionResolveResult.Input as LocalResolveResult;
1578                    }
1579                  } else {
1580                    localVariableResult = parameterResolveResult as LocalResolveResult;
1581                  }
1582
1583                  if (localVariableResult != null && data[localVariableResult.Variable.Name] != NullValueStatus.CapturedUnknown) {
1584                    data = data.Clone();
1585                    data [localVariableResult.Variable.Name] = shouldBeNull ? NullValueStatus.DefinitelyNull : NullValueStatus.DefinitelyNotNull;
1586                  }
1587                }
1588              }
1589            }
1590          }
1591        }
1592
1593        bool isNullable = IsTypeNullable(methodResolveResult.Type);
1594        if (!isNullable) {
1595          return VisitorResult.ForValue(data, NullValueStatus.DefinitelyNotNull);
1596        }
1597
1598        if (method != null)
1599          return VisitorResult.ForValue(data, GetNullableStatus(method));
1600
1601        return VisitorResult.ForValue(data, GetNullableStatus(methodResolveResult.TargetResult.Type.GetDefinition()));
1602      }
1603
1604      static NullValueStatus GetNullableStatus(IEntity entity)
1605      {
1606        if (entity.DeclaringType != null && entity.DeclaringType.Kind == TypeKind.Delegate) {
1607          //Handle Delegate.Invoke method
1608          return GetNullableStatus(entity.DeclaringTypeDefinition);
1609        }
1610       
1611        return GetNullableStatus(fullTypeName => entity.GetAttribute(new FullTypeName(fullTypeName)));
1612      }
1613
1614      static NullValueStatus GetNullableStatus(IParameter parameter)
1615      {
1616        return GetNullableStatus(fullTypeName => parameter.Attributes.FirstOrDefault(attribute => attribute.AttributeType.FullName == fullTypeName));
1617      }
1618
1619      static NullValueStatus GetNullableStatus(Func<string, IAttribute> attributeGetter)
1620      {
1621        if (attributeGetter(AnnotationNames.NotNullAttribute) != null) {
1622          return NullValueStatus.DefinitelyNotNull;
1623        }
1624        if (attributeGetter(AnnotationNames.CanBeNullAttribute) != null) {
1625          return NullValueStatus.PotentiallyNull;
1626        }
1627        return NullValueStatus.Unknown;
1628      }
1629
1630      public override VisitorResult VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, VariableStatusInfo data)
1631      {
1632        var targetResult = memberReferenceExpression.Target.AcceptVisitor(this, data);
1633        if (targetResult.ThrowsException)
1634          return HandleExpressionResult(memberReferenceExpression, targetResult);
1635
1636        var variables = targetResult.Variables;
1637
1638        var memberResolveResult = analysis.context.Resolve(memberReferenceExpression) as MemberResolveResult;
1639
1640        var targetIdentifier = CSharpUtil.GetInnerMostExpression(memberReferenceExpression.Target) as IdentifierExpression;
1641        if (targetIdentifier != null) {
1642          if (memberResolveResult == null) {
1643            var invocation = memberReferenceExpression.Parent as InvocationExpression;
1644            if (invocation != null) {
1645              memberResolveResult = analysis.context.Resolve(invocation) as MemberResolveResult;
1646            }
1647          }
1648
1649          if (memberResolveResult != null && memberResolveResult.Member.FullName != "System.Nullable.HasValue") {
1650            var method = memberResolveResult.Member as IMethod;
1651            if (method == null || !method.IsExtensionMethod) {
1652              if (targetResult.NullableReturnResult == NullValueStatus.DefinitelyNull) {
1653                return HandleExpressionResult(memberReferenceExpression, VisitorResult.ForException(variables));
1654              }
1655              if (variables [targetIdentifier.Identifier] != NullValueStatus.CapturedUnknown) {
1656                variables = variables.Clone();
1657                analysis.SetLocalVariableValue(variables, targetIdentifier, NullValueStatus.DefinitelyNotNull);
1658              }
1659            }
1660          }
1661        }
1662
1663        var returnValue = GetFieldReturnValue(memberResolveResult, data);
1664        return HandleExpressionResult(memberReferenceExpression, variables, returnValue);
1665      }
1666
1667      static NullValueStatus GetFieldReturnValue(MemberResolveResult memberResolveResult, VariableStatusInfo data)
1668      {
1669        bool isNullable = memberResolveResult == null || IsTypeNullable(memberResolveResult.Type);
1670        if (!isNullable) {
1671          return NullValueStatus.DefinitelyNotNull;
1672        }
1673
1674        if (memberResolveResult != null) {
1675          return GetNullableStatus(memberResolveResult.Member);
1676        }
1677
1678        return NullValueStatus.Unknown;
1679      }
1680
1681      public override VisitorResult VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, VariableStatusInfo data)
1682      {
1683        return HandleExpressionResult(typeReferenceExpression, data, NullValueStatus.Unknown);
1684
1685      }
1686
1687      void FixParameter(Expression argument, IList<IParameter> parameters, int parameterIndex, IdentifierExpression identifier, VariableStatusInfo data)
1688      {
1689        NullValueStatus newValue = NullValueStatus.Unknown;
1690        if (argument is NamedArgumentExpression) {
1691          var namedResolveResult = analysis.context.Resolve(argument) as NamedArgumentResolveResult;
1692          if (namedResolveResult != null) {
1693            newValue = GetNullableStatus(namedResolveResult.Parameter);
1694          }
1695        }
1696        else {
1697          var parameter = parameters[parameterIndex];
1698          newValue = GetNullableStatus(parameter);
1699        }
1700        analysis.SetLocalVariableValue(data, identifier, newValue);
1701      }
1702
1703      public override VisitorResult VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, VariableStatusInfo data)
1704      {
1705        foreach (var argumentToHandle in objectCreateExpression.Arguments.Select((argument, parameterIndex) => new { argument, parameterIndex })) {
1706          var argument = argumentToHandle.argument;
1707          var parameterIndex = argumentToHandle.parameterIndex;
1708
1709          var namedArgument = argument as NamedArgumentExpression;
1710
1711          var directionExpression = (namedArgument == null ? argument : namedArgument.Expression) as DirectionExpression;
1712          if (directionExpression != null) {
1713            var identifier = directionExpression.Expression as IdentifierExpression;
1714            if (identifier != null && data [identifier.Identifier] != NullValueStatus.CapturedUnknown) {
1715              //out and ref parameters do *NOT* capture the variable (since they must stop changing it by the time they return)
1716              data = data.Clone();
1717
1718              var constructorResolveResult = analysis.context.Resolve(objectCreateExpression) as CSharpInvocationResolveResult;
1719              if (constructorResolveResult != null)
1720                FixParameter(argument, constructorResolveResult.Member.Parameters, parameterIndex, identifier, data);
1721            }
1722            continue;
1723          }
1724
1725          var argumentResult = argument.AcceptVisitor(this, data);
1726          if (argumentResult.ThrowsException)
1727            return argumentResult;
1728
1729          data = argumentResult.Variables;
1730        }
1731
1732        //Constructors never return null
1733        return HandleExpressionResult(objectCreateExpression, data, NullValueStatus.DefinitelyNotNull);
1734      }
1735
1736      public override VisitorResult VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, VariableStatusInfo data)
1737      {
1738        foreach (var argument in arrayCreateExpression.Arguments) {
1739          var result = argument.AcceptVisitor(this, data);
1740          if (result.ThrowsException)
1741            return result;
1742          data = result.Variables.Clone();
1743        }
1744
1745        if (arrayCreateExpression.Initializer.IsNull) {
1746          return HandleExpressionResult(arrayCreateExpression, data, NullValueStatus.DefinitelyNotNull);
1747        }
1748
1749        return HandleExpressionResult(arrayCreateExpression, arrayCreateExpression.Initializer.AcceptVisitor(this, data));
1750      }
1751
1752      public override VisitorResult VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, VariableStatusInfo data)
1753      {
1754        if (arrayInitializerExpression.IsSingleElement) {
1755          return HandleExpressionResult(arrayInitializerExpression, arrayInitializerExpression.Elements.Single().AcceptVisitor(this, data));
1756        }
1757        if (!arrayInitializerExpression.Elements.Any()) {
1758          //Empty array
1759          return HandleExpressionResult(arrayInitializerExpression, VisitorResult.ForValue(data, NullValueStatus.Unknown));
1760        }
1761
1762        NullValueStatus enumeratedValue = NullValueStatus.UnreachableOrInexistent;
1763        foreach (var element in arrayInitializerExpression.Elements) {
1764          var result = element.AcceptVisitor(this, data);
1765          if (result.ThrowsException)
1766            return result;
1767          data = result.Variables.Clone();
1768          enumeratedValue = VariableStatusInfo.CombineStatus(enumeratedValue, result.NullableReturnResult);
1769
1770        }
1771        return HandleExpressionResult(arrayInitializerExpression, VisitorResult.ForEnumeratedValue(data, enumeratedValue));
1772      }
1773
1774      public override VisitorResult VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, VariableStatusInfo data)
1775      {
1776        foreach (var initializer in anonymousTypeCreateExpression.Initializers) {
1777          var result = initializer.AcceptVisitor(this, data);
1778          if (result.ThrowsException)
1779            return result;
1780          data = result.Variables;
1781        }
1782
1783        return HandleExpressionResult(anonymousTypeCreateExpression, data, NullValueStatus.DefinitelyNotNull);
1784      }
1785
1786      public override VisitorResult VisitLambdaExpression(LambdaExpression lambdaExpression, VariableStatusInfo data)
1787      {
1788        var newData = data.Clone();
1789
1790        var identifiers = lambdaExpression.Descendants.OfType<IdentifierExpression>();
1791        foreach (var identifier in identifiers) {
1792          //Check if it is in a "change-null-state" context
1793          //For instance, x++ does not change the null state
1794          //but `x = y` does.
1795          if (identifier.Parent is AssignmentExpression && identifier.Role == AssignmentExpression.LeftRole) {
1796            var parent = (AssignmentExpression)identifier.Parent;
1797            if (parent.Operator != AssignmentOperatorType.Assign) {
1798              continue;
1799            }
1800          } else {
1801            //No other context matters
1802            //Captured variables are never passed by reference (out/ref)
1803            continue;
1804          }
1805
1806          //At this point, we know there's a good chance the variable has been changed
1807          var identifierResolveResult = analysis.context.Resolve(identifier) as LocalResolveResult;
1808          if (identifierResolveResult != null && IsTypeNullable(identifierResolveResult.Type)) {
1809            analysis.SetLocalVariableValue(newData, identifier, NullValueStatus.CapturedUnknown);
1810          }
1811        }
1812
1813        //The lambda itself is known not to be null
1814        return HandleExpressionResult(lambdaExpression, newData, NullValueStatus.DefinitelyNotNull);
1815      }
1816
1817      public override VisitorResult VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, VariableStatusInfo data)
1818      {
1819        var newData = data.Clone();
1820
1821        var identifiers = anonymousMethodExpression.Descendants.OfType<IdentifierExpression>();
1822        foreach (var identifier in identifiers) {
1823          //Check if it is in a "change-null-state" context
1824          //For instance, x++ does not change the null state
1825          //but `x = y` does.
1826          if (identifier.Parent is AssignmentExpression && identifier.Role == AssignmentExpression.LeftRole) {
1827            var parent = (AssignmentExpression)identifier.Parent;
1828            if (parent.Operator != AssignmentOperatorType.Assign) {
1829              continue;
1830            }
1831          } else {
1832            //No other context matters
1833            //Captured variables are never passed by reference (out/ref)
1834            continue;
1835          }
1836
1837          //At this point, we know there's a good chance the variable has been changed
1838          var identifierResolveResult = analysis.context.Resolve(identifier) as LocalResolveResult;
1839          if (identifierResolveResult != null && IsTypeNullable(identifierResolveResult.Type)) {
1840            analysis.SetLocalVariableValue(newData, identifier, NullValueStatus.CapturedUnknown);
1841          }
1842        }
1843
1844        //The anonymous method itself is known not to be null
1845        return HandleExpressionResult(anonymousMethodExpression, newData, NullValueStatus.DefinitelyNotNull);
1846      }
1847
1848
1849      public override VisitorResult VisitNamedExpression(NamedExpression namedExpression, VariableStatusInfo data)
1850      {
1851        return HandleExpressionResult(namedExpression, namedExpression.Expression.AcceptVisitor(this, data));
1852      }
1853
1854      public override VisitorResult VisitAsExpression(AsExpression asExpression, VariableStatusInfo data)
1855      {
1856        var tentativeResult = asExpression.Expression.AcceptVisitor(this, data);
1857        if (tentativeResult.ThrowsException)
1858          return tentativeResult;
1859
1860        NullValueStatus result;
1861        if (tentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) {
1862          result = NullValueStatus.DefinitelyNull;
1863        } else {
1864          var asResolveResult = analysis.context.Resolve(asExpression) as CastResolveResult;
1865          if (asResolveResult == null ||
1866              asResolveResult.IsError ||
1867              asResolveResult.Input.Type.Kind == TypeKind.Unknown ||
1868              asResolveResult.Type.Kind == TypeKind.Unknown) {
1869
1870            result = NullValueStatus.Unknown;
1871          } else {
1872            var conversion = new CSharpConversions(analysis.context.Compilation);
1873            var foundConversion = conversion.ExplicitConversion(asResolveResult.Input.Type, asResolveResult.Type);
1874
1875            if (foundConversion == Conversion.None) {
1876              result = NullValueStatus.DefinitelyNull;
1877            } else if (foundConversion == Conversion.IdentityConversion) {
1878              result = tentativeResult.NullableReturnResult;
1879            } else {
1880              result = NullValueStatus.PotentiallyNull;
1881            }
1882          }
1883        }
1884        return HandleExpressionResult(asExpression, tentativeResult.Variables, result);
1885      }
1886
1887      public override VisitorResult VisitCastExpression(CastExpression castExpression, VariableStatusInfo data)
1888      {
1889        var tentativeResult = castExpression.Expression.AcceptVisitor(this, data);
1890        if (tentativeResult.ThrowsException)
1891          return tentativeResult;
1892
1893        NullValueStatus result;
1894        if (tentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull) {
1895          result = NullValueStatus.DefinitelyNull;
1896        } else {
1897          result = NullValueStatus.Unknown;
1898        }
1899
1900        VariableStatusInfo variables = tentativeResult.Variables;
1901
1902        var resolveResult = analysis.context.Resolve(castExpression) as CastResolveResult;
1903        if (resolveResult != null && !IsTypeNullable(resolveResult.Type)) {
1904          if (result == NullValueStatus.DefinitelyNull) {
1905            return HandleExpressionResult(castExpression, VisitorResult.ForException(tentativeResult.Variables));
1906          }
1907
1908          var identifierExpression = CSharpUtil.GetInnerMostExpression(castExpression.Expression) as IdentifierExpression;
1909          if (identifierExpression != null) {
1910            var currentValue = variables [identifierExpression.Identifier];
1911            if (currentValue != NullValueStatus.CapturedUnknown &&
1912                currentValue != NullValueStatus.UnreachableOrInexistent &&
1913                currentValue != NullValueStatus.DefinitelyNotNull) {
1914              //DefinitelyNotNull is included in this list because if that's the status
1915              // then we don't need to change anything
1916
1917              variables = variables.Clone();
1918              variables [identifierExpression.Identifier] = NullValueStatus.DefinitelyNotNull;
1919            }
1920          }
1921
1922          result = NullValueStatus.DefinitelyNotNull;
1923        }
1924
1925        return HandleExpressionResult(castExpression, variables, result);
1926      }
1927
1928      public override VisitorResult VisitIsExpression(IsExpression isExpression, VariableStatusInfo data)
1929      {
1930        var tentativeResult = isExpression.Expression.AcceptVisitor(this, data);
1931        if (tentativeResult.ThrowsException)
1932          return tentativeResult;
1933
1934        //TODO: Consider, for instance: new X() is X. The result is known to be true, so we can use KnownBoolValue
1935        return HandleExpressionResult(isExpression, tentativeResult.Variables, NullValueStatus.DefinitelyNotNull);
1936      }
1937
1938      public override VisitorResult VisitDirectionExpression(DirectionExpression directionExpression, VariableStatusInfo data)
1939      {
1940        return HandleExpressionResult(directionExpression, directionExpression.Expression.AcceptVisitor(this, data));
1941      }
1942
1943      public override VisitorResult VisitCheckedExpression(CheckedExpression checkedExpression, VariableStatusInfo data)
1944      {
1945        return HandleExpressionResult(checkedExpression, checkedExpression.Expression.AcceptVisitor(this, data));
1946      }
1947
1948      public override VisitorResult VisitUncheckedExpression(UncheckedExpression uncheckedExpression, VariableStatusInfo data)
1949      {
1950        return HandleExpressionResult(uncheckedExpression, uncheckedExpression.Expression.AcceptVisitor(this, data));
1951      }
1952
1953      public override VisitorResult VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, VariableStatusInfo data)
1954      {
1955        return HandleExpressionResult(thisReferenceExpression, data, NullValueStatus.DefinitelyNotNull);
1956      }
1957
1958      public override VisitorResult VisitIndexerExpression(IndexerExpression indexerExpression, VariableStatusInfo data)
1959      {
1960        var tentativeResult = indexerExpression.Target.AcceptVisitor(this, data);
1961        if (tentativeResult.ThrowsException)
1962          return tentativeResult;
1963
1964        data = tentativeResult.Variables;
1965
1966        foreach (var argument in indexerExpression.Arguments) {
1967          var result = argument.AcceptVisitor(this, data);
1968          if (result.ThrowsException)
1969            return result;
1970          data = result.Variables.Clone();
1971        }
1972
1973        IdentifierExpression targetAsIdentifier = CSharpUtil.GetInnerMostExpression(indexerExpression.Target) as IdentifierExpression;
1974        if (targetAsIdentifier != null) {
1975          if (tentativeResult.NullableReturnResult == NullValueStatus.DefinitelyNull)
1976            return HandleExpressionResult(indexerExpression, VisitorResult.ForException(data));
1977
1978          //If this doesn't cause an exception, then the target is not null
1979          //But we won't set it if it has been changed
1980          var descendentIdentifiers = indexerExpression.Arguments
1981            .SelectMany(argument => argument.DescendantsAndSelf).OfType<IdentifierExpression>();
1982          if (!descendentIdentifiers.Any(identifier => identifier.Identifier == targetAsIdentifier.Identifier)) {
1983            //TODO: this check might be improved to include more legitimate cases
1984            //A good check will necessarily have to consider captured variables
1985            data = data.Clone();
1986            analysis.SetLocalVariableValue(data, targetAsIdentifier, NullValueStatus.DefinitelyNotNull);
1987          }
1988        }
1989
1990        var indexerResolveResult = analysis.context.Resolve(indexerExpression) as CSharpInvocationResolveResult;
1991        bool isNullable = indexerResolveResult == null || IsTypeNullable(indexerResolveResult.Type);
1992       
1993        var returnValue = isNullable ? NullValueStatus.Unknown : NullValueStatus.DefinitelyNotNull;
1994        return HandleExpressionResult(indexerExpression, data, returnValue);
1995      }
1996
1997      public override VisitorResult VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, VariableStatusInfo data)
1998      {
1999        return HandleExpressionResult(baseReferenceExpression, data, NullValueStatus.DefinitelyNotNull);
2000      }
2001
2002      public override VisitorResult VisitTypeOfExpression(TypeOfExpression typeOfExpression, VariableStatusInfo data)
2003      {
2004        return HandleExpressionResult(typeOfExpression, data, NullValueStatus.DefinitelyNotNull);
2005      }
2006
2007      public override VisitorResult VisitSizeOfExpression(SizeOfExpression sizeOfExpression, VariableStatusInfo data)
2008      {
2009        return HandleExpressionResult(sizeOfExpression, data, NullValueStatus.DefinitelyNotNull);
2010      }
2011
2012      public override VisitorResult VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, VariableStatusInfo data)
2013      {
2014        var targetResult = pointerReferenceExpression.Target.AcceptVisitor(this, data);
2015        if (targetResult.ThrowsException)
2016          return targetResult;
2017        return HandleExpressionResult(pointerReferenceExpression, targetResult.Variables, NullValueStatus.DefinitelyNotNull);
2018      }
2019
2020      public override VisitorResult VisitStackAllocExpression(StackAllocExpression stackAllocExpression, VariableStatusInfo data)
2021      {
2022        var countResult = stackAllocExpression.CountExpression.AcceptVisitor(this, data);
2023        if (countResult.ThrowsException)
2024          return countResult;
2025        return HandleExpressionResult(stackAllocExpression, countResult.Variables, NullValueStatus.DefinitelyNotNull);
2026      }
2027
2028      public override VisitorResult VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, VariableStatusInfo data)
2029      {
2030        return HandleExpressionResult(namedArgumentExpression, namedArgumentExpression.Expression.AcceptVisitor(this, data));
2031      }
2032
2033      public override VisitorResult VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression, VariableStatusInfo data)
2034      {
2035        throw new NotImplementedException();
2036      }
2037
2038      public override VisitorResult VisitQueryExpression(QueryExpression queryExpression, VariableStatusInfo data)
2039      {
2040        VariableStatusInfo outgoingData = data.Clone();
2041        NullValueStatus? outgoingEnumeratedValue = null;
2042        var clauses = queryExpression.Clauses.ToList();
2043
2044        var backtracingClauses = (from item in clauses.Select((clause, i) => new { clause, i })
2045                                        where item.clause is QueryFromClause || item.clause is QueryJoinClause || item.clause is QueryContinuationClause
2046                                        select item.i).ToList();
2047
2048        var beforeClauseVariableStates = Enumerable.Range(0, clauses.Count).ToDictionary(clauseIndex => clauseIndex,
2049                                                                                         clauseIndex => new VariableStatusInfo());
2050        var afterClauseVariableStates = Enumerable.Range(0, clauses.Count).ToDictionary(clauseIndex => clauseIndex,
2051                                                                                        clauseIndex => new VariableStatusInfo());
2052
2053        VisitorResult lastValidResult = null;
2054        int currentClauseIndex = 0;
2055        for (;;) {
2056          VisitorResult result = null;
2057          QueryClause clause = null;
2058          bool backtrack = false;
2059
2060          if (currentClauseIndex >= clauses.Count) {
2061            backtrack = true;
2062          } else {
2063            clause = clauses [currentClauseIndex];
2064            beforeClauseVariableStates [currentClauseIndex].ReceiveIncoming(data);
2065            result = clause.AcceptVisitor(this, data);
2066            data = result.Variables;
2067            lastValidResult = result;
2068            if (result.KnownBoolResult == false) {
2069              backtrack = true;
2070            }
2071            if (result.ThrowsException) {
2072              //Don't backtrack. Exceptions completely stop the query.
2073              break;
2074            }
2075            else {
2076              afterClauseVariableStates [currentClauseIndex].ReceiveIncoming(data);
2077            }
2078          }
2079
2080          if (backtrack) {
2081            int? newIndex;
2082            for (;;) {
2083              newIndex = backtracingClauses.LastOrDefault(index => index < currentClauseIndex);
2084              if (newIndex == null) {
2085                //We've reached the end
2086                break;
2087              }
2088
2089              currentClauseIndex = (int)newIndex + 1;
2090
2091              if (!beforeClauseVariableStates[currentClauseIndex].ReceiveIncoming(lastValidResult.Variables)) {
2092                newIndex = null;
2093                break;
2094              }
2095            }
2096
2097            if (newIndex == null) {
2098              break;
2099            }
2100
2101          } else {
2102            if (clause is QuerySelectClause) {
2103              outgoingData.ReceiveIncoming(data);
2104              if (outgoingEnumeratedValue == null)
2105                outgoingEnumeratedValue = result.EnumeratedValueResult;
2106              else
2107                outgoingEnumeratedValue = VariableStatusInfo.CombineStatus(outgoingEnumeratedValue.Value, result.EnumeratedValueResult);
2108            }
2109
2110            ++currentClauseIndex;
2111          }
2112        }
2113
2114        var finalData = new VariableStatusInfo();
2115        var endingClauseIndices = from item in clauses.Select((clause, i) => new { clause, i })
2116          let clause = item.clause
2117            where clause is QueryFromClause ||
2118          clause is QueryContinuationClause ||
2119          clause is QueryJoinClause ||
2120          clause is QuerySelectClause ||
2121          clause is QueryWhereClause
2122            select item.i;
2123        foreach (var clauseIndex in endingClauseIndices) {
2124          finalData.ReceiveIncoming(afterClauseVariableStates [clauseIndex]);
2125        }
2126
2127        return VisitorResult.ForEnumeratedValue(finalData, outgoingEnumeratedValue ?? NullValueStatus.Unknown);
2128      }
2129
2130      public override VisitorResult VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, VariableStatusInfo data)
2131      {
2132        return IntroduceVariableFromEnumeratedValue(queryContinuationClause.Identifier, queryContinuationClause.PrecedingQuery, data);
2133      }
2134
2135      VisitorResult IntroduceVariableFromEnumeratedValue(string newVariable, Expression expression, VariableStatusInfo data)
2136      {
2137        var result = expression.AcceptVisitor(this, data);
2138        var newVariables = result.Variables.Clone();
2139        newVariables[newVariable] = result.EnumeratedValueResult;
2140        return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown);
2141      }
2142
2143      public override VisitorResult VisitQueryFromClause(QueryFromClause queryFromClause, VariableStatusInfo data)
2144      {
2145        return IntroduceVariableFromEnumeratedValue(queryFromClause.Identifier, queryFromClause.Expression, data);
2146      }
2147
2148      public override VisitorResult VisitQueryJoinClause(QueryJoinClause queryJoinClause, VariableStatusInfo data)
2149      {
2150        //TODO: Check if this really works in weird edge-cases.
2151        var tentativeResult = IntroduceVariableFromEnumeratedValue(queryJoinClause.JoinIdentifier, queryJoinClause.InExpression, data);
2152        tentativeResult = queryJoinClause.OnExpression.AcceptVisitor(this, tentativeResult.Variables);
2153        tentativeResult = queryJoinClause.EqualsExpression.AcceptVisitor(this, tentativeResult.Variables);
2154
2155        if (queryJoinClause.IsGroupJoin) {
2156          var newVariables = tentativeResult.Variables.Clone();
2157          analysis.SetLocalVariableValue(newVariables, queryJoinClause.IntoIdentifierToken, NullValueStatus.DefinitelyNotNull);
2158          return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown);
2159        }
2160
2161        return tentativeResult;
2162      }
2163
2164      public override VisitorResult VisitQueryLetClause(QueryLetClause queryLetClause, VariableStatusInfo data)
2165      {
2166        var result = queryLetClause.Expression.AcceptVisitor(this, data);
2167
2168        string newVariable = queryLetClause.Identifier;
2169        var newVariables = result.Variables.Clone();
2170        newVariables [newVariable] = result.NullableReturnResult;
2171
2172        return VisitorResult.ForValue(newVariables, NullValueStatus.Unknown);
2173      }
2174
2175      public override VisitorResult VisitQuerySelectClause(QuerySelectClause querySelectClause, VariableStatusInfo data)
2176      {
2177        var result = querySelectClause.Expression.AcceptVisitor(this, data);
2178
2179        //The value of the expression in select becomes the "enumerated" value
2180        return VisitorResult.ForEnumeratedValue(result.Variables, result.NullableReturnResult);
2181      }
2182
2183      public override VisitorResult VisitQueryWhereClause(QueryWhereClause queryWhereClause, VariableStatusInfo data)
2184      {
2185        var result = queryWhereClause.Condition.AcceptVisitor(this, data);
2186
2187        return VisitorResult.ForEnumeratedValue(result.TruePathVariables, NullValueStatus.Unknown);
2188      }
2189
2190      public override VisitorResult VisitQueryOrderClause(QueryOrderClause queryOrderClause, VariableStatusInfo data)
2191      {
2192        foreach (var ordering in queryOrderClause.Orderings) {
2193          data = ordering.AcceptVisitor(this, data).Variables;
2194        }
2195
2196        return VisitorResult.ForValue(data, NullValueStatus.Unknown);
2197      }
2198
2199      public override VisitorResult VisitQueryOrdering(QueryOrdering queryOrdering, VariableStatusInfo data)
2200      {
2201        return VisitorResult.ForValue(queryOrdering.Expression.AcceptVisitor(this, data).Variables, NullValueStatus.Unknown);
2202      }
2203
2204      public override VisitorResult VisitQueryGroupClause(QueryGroupClause queryGroupClause, VariableStatusInfo data)
2205      {
2206        var projectionResult = queryGroupClause.Projection.AcceptVisitor(this, data);
2207        data = projectionResult.Variables;
2208        data = queryGroupClause.Key.AcceptVisitor(this, data).Variables;
2209
2210        return VisitorResult.ForEnumeratedValue(data, projectionResult.NullableReturnResult);
2211      }
2212    }
2213  }
2214}
2215
Note: See TracBrowser for help on using the repository browser.