1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
3
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
// software and associated documentation files (the "Software"), to deal in the Software
5
// without restriction, including without limitation the rights to use, copy, modify, merge,
6
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7
// to whom the Software is furnished to do so, subject to the following conditions:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
12
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17
// DEALINGS IN THE SOFTWARE.
20
using System.Collections.Generic;
21
using System.Diagnostics;
23
using System.Threading;
25
using ICSharpCode.NRefactory.CSharp.Resolver;
26
using ICSharpCode.NRefactory.Semantics;
27
using ICSharpCode.NRefactory.TypeSystem;
28
using ICSharpCode.NRefactory.TypeSystem.Implementation;
29
using ICSharpCode.NRefactory.Utils;
31
namespace ICSharpCode.NRefactory.CSharp.Analysis
34
/// Represents the definite assignment status of a variable at a specific location.
36
public enum DefiniteAssignmentStatus
39
/// The variable might be assigned or unassigned.
43
/// The variable is definitely assigned.
47
/// The variable is definitely assigned iff the expression results in the value 'true'.
49
AssignedAfterTrueExpression,
51
/// The variable is definitely assigned iff the expression results in the value 'false'.
53
AssignedAfterFalseExpression,
55
/// The code is unreachable.
61
/// Implements the C# definite assignment analysis (C# 4.0 Spec: §5.3 Definite assignment)
63
public class DefiniteAssignmentAnalysis
65
sealed class DefiniteAssignmentNode : ControlFlowNode
68
public DefiniteAssignmentStatus NodeStatus;
70
public DefiniteAssignmentNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
71
: base(previousStatement, nextStatement, type)
76
sealed class DerivedControlFlowGraphBuilder : ControlFlowGraphBuilder
78
protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
80
return new DefiniteAssignmentNode(previousStatement, nextStatement, type);
84
readonly DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor();
85
readonly List<DefiniteAssignmentNode> allNodes = new List<DefiniteAssignmentNode>();
86
readonly Dictionary<Statement, DefiniteAssignmentNode> beginNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
87
readonly Dictionary<Statement, DefiniteAssignmentNode> endNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
88
readonly Dictionary<Statement, DefiniteAssignmentNode> conditionNodeDict = new Dictionary<Statement, DefiniteAssignmentNode>();
89
readonly CSharpAstResolver resolver;
90
Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>();
93
List<IdentifierExpression> unassignedVariableUses = new List<IdentifierExpression>();
94
int analyzedRangeStart, analyzedRangeEnd;
95
CancellationToken analysisCancellationToken;
97
Queue<DefiniteAssignmentNode> nodesWithModifiedInput = new Queue<DefiniteAssignmentNode>();
99
public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken)
100
: this(rootStatement,
101
new CSharpAstResolver(new CSharpResolver(MinimalCorlib.Instance.CreateCompilation()), rootStatement),
106
public DefiniteAssignmentAnalysis(Statement rootStatement, CSharpAstResolver resolver, CancellationToken cancellationToken)
108
if (rootStatement == null)
109
throw new ArgumentNullException("rootStatement");
110
if (resolver == null)
111
throw new ArgumentNullException("resolver");
112
this.resolver = resolver;
114
visitor.analysis = this;
115
DerivedControlFlowGraphBuilder cfgBuilder = new DerivedControlFlowGraphBuilder();
116
if (resolver.TypeResolveContext.Compilation.MainAssembly.UnresolvedAssembly is MinimalCorlib) {
117
cfgBuilder.EvaluateOnlyPrimitiveConstants = true;
119
allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolver, cancellationToken).Cast<DefiniteAssignmentNode>());
120
for (int i = 0; i < allNodes.Count; i++) {
121
DefiniteAssignmentNode node = allNodes[i];
122
node.Index = i; // assign numbers to the nodes
123
if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) {
124
// Anonymous methods have separate control flow graphs, but we also need to analyze those.
125
// Iterate backwards so that anonymous methods are inserted in the correct order
126
for (AstNode child = node.NextStatement.LastChild; child != null; child = child.PrevSibling) {
127
InsertAnonymousMethods(i + 1, child, cfgBuilder, cancellationToken);
130
// Now register the node in the dictionaries:
131
if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements)
132
beginNodeDict.Add(node.NextStatement, node);
133
if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode)
134
endNodeDict.Add(node.PreviousStatement, node);
135
if (node.Type == ControlFlowNodeType.LoopCondition)
136
conditionNodeDict.Add(node.NextStatement, node);
138
// Verify that we created nodes for all statements:
139
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
140
// Verify that we put all nodes into the dictionaries:
141
Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().All(stmt => beginNodeDict.ContainsKey(stmt)));
142
Debug.Assert(rootStatement.DescendantsAndSelf.OfType<Statement>().All(stmt => endNodeDict.ContainsKey(stmt)));
144
this.analyzedRangeStart = 0;
145
this.analyzedRangeEnd = allNodes.Count - 1;
148
void InsertAnonymousMethods(int insertPos, AstNode node, ControlFlowGraphBuilder cfgBuilder, CancellationToken cancellationToken)
150
// Ignore any statements, as those have their own ControlFlowNode and get handled separately
151
if (node is Statement)
153
AnonymousMethodExpression ame = node as AnonymousMethodExpression;
155
allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph(ame.Body, resolver, cancellationToken).Cast<DefiniteAssignmentNode>());
158
LambdaExpression lambda = node as LambdaExpression;
159
if (lambda != null && lambda.Body is Statement) {
160
allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolver, cancellationToken).Cast<DefiniteAssignmentNode>());
163
// Descend into child expressions
164
// Iterate backwards so that anonymous methods are inserted in the correct order
165
for (AstNode child = node.LastChild; child != null; child = child.PrevSibling) {
166
InsertAnonymousMethods(insertPos, child, cfgBuilder, cancellationToken);
171
/// Gets the unassigned usages of the previously analyzed variable.
173
public IList<IdentifierExpression> UnassignedVariableUses {
175
return unassignedVariableUses.AsReadOnly();
180
/// Sets the range of statements to be analyzed.
181
/// This method can be used to restrict the analysis to only a part of the method.
182
/// Only the control flow paths that are fully contained within the selected part will be analyzed.
184
/// <remarks>By default, both 'start' and 'end' are inclusive.</remarks>
185
public void SetAnalyzedRange(Statement start, Statement end, bool startInclusive = true, bool endInclusive = true)
187
var dictForStart = startInclusive ? beginNodeDict : endNodeDict;
188
var dictForEnd = endInclusive ? endNodeDict : beginNodeDict;
189
Debug.Assert(dictForStart.ContainsKey(start) && dictForEnd.ContainsKey(end));
190
int startIndex = dictForStart[start].Index;
191
int endIndex = dictForEnd[end].Index;
192
if (startIndex > endIndex)
193
throw new ArgumentException("The start statement must be lexically preceding the end statement");
194
this.analyzedRangeStart = startIndex;
195
this.analyzedRangeEnd = endIndex;
198
public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned, CancellationToken cancellationToken = default(CancellationToken))
200
this.analysisCancellationToken = cancellationToken;
201
this.variableName = variable;
204
unassignedVariableUses.Clear();
205
foreach (DefiniteAssignmentNode node in allNodes) {
206
node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable;
207
foreach (ControlFlowEdge edge in node.Outgoing)
208
edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable;
211
ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus);
212
// Iterate as long as the input status of some nodes is changing:
213
while (nodesWithModifiedInput.Count > 0) {
214
DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue();
215
DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable;
216
foreach (ControlFlowEdge edge in node.Incoming) {
217
inputStatus = MergeStatus(inputStatus, edgeStatus[edge]);
219
ChangeNodeStatus(node, inputStatus);
222
this.analysisCancellationToken = CancellationToken.None;
223
this.variableName = null;
227
public DefiniteAssignmentStatus GetStatusBefore(Statement statement)
229
return beginNodeDict[statement].NodeStatus;
232
public DefiniteAssignmentStatus GetStatusAfter(Statement statement)
234
return endNodeDict[statement].NodeStatus;
237
public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement)
239
return conditionNodeDict[statement].NodeStatus;
243
/// Exports the CFG. This method is intended to help debugging issues related to definite assignment.
245
public GraphVizGraph ExportGraph()
247
GraphVizGraph g = new GraphVizGraph();
248
g.Title = "DefiniteAssignment - " + variableName;
249
for (int i = 0; i < allNodes.Count; i++) {
250
string name = "#" + i + " = " + allNodes[i].NodeStatus.ToString() + Environment.NewLine;
251
switch (allNodes[i].Type) {
252
case ControlFlowNodeType.StartNode:
253
case ControlFlowNodeType.BetweenStatements:
254
name += allNodes[i].NextStatement.ToString();
256
case ControlFlowNodeType.EndNode:
257
name += "End of " + allNodes[i].PreviousStatement.ToString();
259
case ControlFlowNodeType.LoopCondition:
260
name += "Condition in " + allNodes[i].NextStatement.ToString();
263
name += allNodes[i].Type.ToString();
266
g.AddNode(new GraphVizNode(i) { label = name });
267
foreach (ControlFlowEdge edge in allNodes[i].Outgoing) {
268
GraphVizEdge ge = new GraphVizEdge(i, ((DefiniteAssignmentNode)edge.To).Index);
269
if (edgeStatus.Count > 0)
270
ge.label = edgeStatus[edge].ToString();
271
if (edge.IsLeavingTryFinally)
274
case ControlFlowEdgeType.ConditionTrue:
277
case ControlFlowEdgeType.ConditionFalse:
280
case ControlFlowEdgeType.Jump:
290
static DefiniteAssignmentStatus MergeStatus(DefiniteAssignmentStatus a, DefiniteAssignmentStatus b)
292
// The result will be DefinitelyAssigned if at least one incoming edge is DefinitelyAssigned and all others are unreachable.
293
// The result will be DefinitelyUnassigned if at least one incoming edge is DefinitelyUnassigned and all others are unreachable.
294
// The result will be Unreachable if all incoming edges are unreachable.
295
// Otherwise, the result will be PotentiallyAssigned.
299
else if (a == DefiniteAssignmentStatus.CodeUnreachable)
301
else if (b == DefiniteAssignmentStatus.CodeUnreachable)
304
return DefiniteAssignmentStatus.PotentiallyAssigned;
307
void ChangeNodeStatus (DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus)
309
if (node.NodeStatus == inputStatus)
311
node.NodeStatus = inputStatus;
312
DefiniteAssignmentStatus outputStatus;
314
case ControlFlowNodeType.StartNode:
315
case ControlFlowNodeType.BetweenStatements:
316
if (node.NextStatement is IfElseStatement) {
317
// Handle if-else as a condition node
318
goto case ControlFlowNodeType.LoopCondition;
320
if (inputStatus == DefiniteAssignmentStatus.DefinitelyAssigned) {
321
// There isn't any way to un-assign variables, so we don't have to check the expression
322
// if the status already is definitely assigned.
323
outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned;
325
outputStatus = CleanSpecialValues (node.NextStatement.AcceptVisitor (visitor, inputStatus));
328
case ControlFlowNodeType.EndNode:
329
outputStatus = inputStatus;
330
if (node.PreviousStatement.Role == TryCatchStatement.FinallyBlockRole
331
&& (outputStatus == DefiniteAssignmentStatus.DefinitelyAssigned || outputStatus == DefiniteAssignmentStatus.PotentiallyAssigned)) {
332
TryCatchStatement tryFinally = (TryCatchStatement)node.PreviousStatement.Parent;
333
// Changing the status on a finally block potentially changes the status of all edges leaving that finally block:
334
foreach (ControlFlowEdge edge in allNodes.SelectMany(n => n.Outgoing)) {
335
if (edge.IsLeavingTryFinally && edge.TryFinallyStatements.Contains (tryFinally)) {
336
DefiniteAssignmentStatus s = edgeStatus [edge];
337
if (s == DefiniteAssignmentStatus.PotentiallyAssigned) {
338
ChangeEdgeStatus (edge, outputStatus);
344
case ControlFlowNodeType.LoopCondition:
345
ForeachStatement foreachStmt = node.NextStatement as ForeachStatement;
346
if (foreachStmt != null) {
347
outputStatus = CleanSpecialValues (foreachStmt.InExpression.AcceptVisitor (visitor, inputStatus));
348
if (foreachStmt.VariableName == this.variableName)
349
outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned;
352
Debug.Assert (node.NextStatement is IfElseStatement || node.NextStatement is WhileStatement || node.NextStatement is ForStatement || node.NextStatement is DoWhileStatement);
353
Expression condition = node.NextStatement.GetChildByRole (Roles.Condition);
354
if (condition.IsNull)
355
outputStatus = inputStatus;
357
outputStatus = condition.AcceptVisitor(visitor, inputStatus);
358
foreach (ControlFlowEdge edge in node.Outgoing) {
359
if (edge.Type == ControlFlowEdgeType.ConditionTrue && outputStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) {
360
ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned);
361
} else if (edge.Type == ControlFlowEdgeType.ConditionFalse && outputStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression) {
362
ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned);
364
ChangeEdgeStatus(edge, CleanSpecialValues(outputStatus));
370
throw new InvalidOperationException();
372
foreach (ControlFlowEdge edge in node.Outgoing) {
373
ChangeEdgeStatus(edge, outputStatus);
377
void ChangeEdgeStatus(ControlFlowEdge edge, DefiniteAssignmentStatus newStatus)
379
DefiniteAssignmentStatus oldStatus = edgeStatus[edge];
380
if (oldStatus == newStatus)
382
// Ensure that status can cannot change back to CodeUnreachable after it once was reachable.
383
// Also, don't ever use AssignedAfter... for statements.
384
if (newStatus == DefiniteAssignmentStatus.CodeUnreachable
385
|| newStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression
386
|| newStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
388
throw new InvalidOperationException();
390
// Note that the status can change from DefinitelyAssigned
391
// back to PotentiallyAssigned as unreachable input edges are
392
// discovered to be reachable.
394
edgeStatus[edge] = newStatus;
395
DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To;
396
if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd) {
397
// TODO: potential optimization: visit previously unreachable nodes with higher priority
398
// (e.g. use Deque and enqueue previously unreachable nodes at the front, but
399
// other nodes at the end)
400
nodesWithModifiedInput.Enqueue(targetNode);
405
/// Evaluates an expression.
407
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
408
ResolveResult EvaluateConstant(Expression expr)
410
return resolver.Resolve(expr, analysisCancellationToken);
414
/// Evaluates an expression.
416
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
417
bool? EvaluateCondition(Expression expr)
419
ResolveResult rr = EvaluateConstant(expr);
420
if (rr != null && rr.IsCompileTimeConstant)
421
return rr.ConstantValue as bool?;
426
static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status)
428
if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
429
return DefiniteAssignmentStatus.PotentiallyAssigned;
430
else if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
431
return DefiniteAssignmentStatus.PotentiallyAssigned;
436
sealed class DefiniteAssignmentVisitor : DepthFirstAstVisitor<DefiniteAssignmentStatus, DefiniteAssignmentStatus>
438
internal DefiniteAssignmentAnalysis analysis;
440
// The general approach for unknown nodes is to pass the status through all child nodes in order
441
protected override DefiniteAssignmentStatus VisitChildren(AstNode node, DefiniteAssignmentStatus data)
443
// the special values are valid as output only, not as input
444
Debug.Assert(data == CleanSpecialValues(data));
445
DefiniteAssignmentStatus status = data;
446
foreach (AstNode child in node.Children) {
447
analysis.analysisCancellationToken.ThrowIfCancellationRequested();
449
Debug.Assert(!(child is Statement)); // statements are visited with the CFG, not with the visitor pattern
450
status = child.AcceptVisitor(this, status);
451
status = CleanSpecialValues(status);
457
// For statements, the visitor only describes the effect of the statement itself;
458
// we do not consider the effect of any nested statements.
459
// This is done because the nested statements will be reached using the control flow graph.
461
// In fact, these methods are present so that the default logic in VisitChildren does not try to visit the nested statements.
463
public override DefiniteAssignmentStatus VisitBlockStatement(BlockStatement blockStatement, DefiniteAssignmentStatus data)
468
public override DefiniteAssignmentStatus VisitCheckedStatement(CheckedStatement checkedStatement, DefiniteAssignmentStatus data)
473
public override DefiniteAssignmentStatus VisitUncheckedStatement(UncheckedStatement uncheckedStatement, DefiniteAssignmentStatus data)
478
// ExpressionStatement handled by default logic
479
// VariableDeclarationStatement handled by default logic
481
public override DefiniteAssignmentStatus VisitVariableInitializer(VariableInitializer variableInitializer, DefiniteAssignmentStatus data)
483
if (variableInitializer.Initializer.IsNull) {
486
DefiniteAssignmentStatus status = variableInitializer.Initializer.AcceptVisitor(this, data);
487
if (variableInitializer.Name == analysis.variableName)
488
return DefiniteAssignmentStatus.DefinitelyAssigned;
494
// IfStatement not handled by visitor, but special-cased in the code consuming the control flow graph
496
public override DefiniteAssignmentStatus VisitSwitchStatement(SwitchStatement switchStatement, DefiniteAssignmentStatus data)
498
return switchStatement.Expression.AcceptVisitor(this, data);
501
public override DefiniteAssignmentStatus VisitWhileStatement(WhileStatement whileStatement, DefiniteAssignmentStatus data)
503
return data; // condition is handled by special condition CFG node
506
public override DefiniteAssignmentStatus VisitDoWhileStatement(DoWhileStatement doWhileStatement, DefiniteAssignmentStatus data)
508
return data; // condition is handled by special condition CFG node
511
public override DefiniteAssignmentStatus VisitForStatement(ForStatement forStatement, DefiniteAssignmentStatus data)
513
return data; // condition is handled by special condition CFG node; initializer and iterator statements are handled by CFG
516
// Break/Continue/Goto: handled by default logic
518
// ThrowStatement: handled by default logic (just visit the expression)
519
// ReturnStatement: handled by default logic (just visit the expression)
521
public override DefiniteAssignmentStatus VisitTryCatchStatement(TryCatchStatement tryCatchStatement, DefiniteAssignmentStatus data)
523
return data; // no special logic when entering the try-catch-finally statement
524
// TODO: where to put the special logic when exiting the try-finally statement?
527
public override DefiniteAssignmentStatus VisitForeachStatement(ForeachStatement foreachStatement, DefiniteAssignmentStatus data)
529
return data; // assignment of the foreach loop variable is done when handling the condition node
532
public override DefiniteAssignmentStatus VisitUsingStatement(UsingStatement usingStatement, DefiniteAssignmentStatus data)
534
if (usingStatement.ResourceAcquisition is Expression)
535
return usingStatement.ResourceAcquisition.AcceptVisitor(this, data);
537
return data; // don't handle resource acquisition statements, as those are connected in the control flow graph
540
public override DefiniteAssignmentStatus VisitLockStatement(LockStatement lockStatement, DefiniteAssignmentStatus data)
542
return lockStatement.Expression.AcceptVisitor(this, data);
545
// Yield statements use the default logic
547
public override DefiniteAssignmentStatus VisitUnsafeStatement(UnsafeStatement unsafeStatement, DefiniteAssignmentStatus data)
552
public override DefiniteAssignmentStatus VisitFixedStatement(FixedStatement fixedStatement, DefiniteAssignmentStatus data)
554
DefiniteAssignmentStatus status = data;
555
foreach (var variable in fixedStatement.Variables)
556
status = variable.AcceptVisitor(this, status);
562
public override DefiniteAssignmentStatus VisitDirectionExpression(DirectionExpression directionExpression, DefiniteAssignmentStatus data)
564
if (directionExpression.FieldDirection == FieldDirection.Out) {
565
return HandleAssignment(directionExpression.Expression, null, data);
567
// use default logic for 'ref'
568
return VisitChildren(directionExpression, data);
572
public override DefiniteAssignmentStatus VisitAssignmentExpression(AssignmentExpression assignmentExpression, DefiniteAssignmentStatus data)
574
if (assignmentExpression.Operator == AssignmentOperatorType.Assign) {
575
return HandleAssignment(assignmentExpression.Left, assignmentExpression.Right, data);
577
// use default logic for compound assignment operators
578
return VisitChildren(assignmentExpression, data);
582
DefiniteAssignmentStatus HandleAssignment(Expression left, Expression right, DefiniteAssignmentStatus initialStatus)
584
IdentifierExpression ident = left as IdentifierExpression;
585
if (ident != null && ident.Identifier == analysis.variableName) {
586
// right==null is special case when handling 'out' expressions
588
right.AcceptVisitor(this, initialStatus);
589
return DefiniteAssignmentStatus.DefinitelyAssigned;
591
DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus);
593
status = right.AcceptVisitor(this, CleanSpecialValues(status));
594
return CleanSpecialValues(status);
598
public override DefiniteAssignmentStatus VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, DefiniteAssignmentStatus data)
600
// Don't use the default logic here because we don't want to clean up the special values.
601
return parenthesizedExpression.Expression.AcceptVisitor(this, data);
604
public override DefiniteAssignmentStatus VisitCheckedExpression(CheckedExpression checkedExpression, DefiniteAssignmentStatus data)
606
return checkedExpression.Expression.AcceptVisitor(this, data);
609
public override DefiniteAssignmentStatus VisitUncheckedExpression(UncheckedExpression uncheckedExpression, DefiniteAssignmentStatus data)
611
return uncheckedExpression.Expression.AcceptVisitor(this, data);
614
public override DefiniteAssignmentStatus VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, DefiniteAssignmentStatus data)
616
if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalAnd) {
617
// Handle constant left side of && expressions (not in the C# spec, but done by the MS compiler)
618
bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left);
620
return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally
621
else if (cond == false)
622
return data; // right operand never gets evaluated
623
// C# 4.0 spec: §5.3.3.24 Definite Assignment for && expressions
624
DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data);
625
DefiniteAssignmentStatus beforeRight;
626
if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
627
beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned;
628
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
629
beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned;
631
beforeRight = afterLeft;
632
DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight);
633
if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned)
634
return DefiniteAssignmentStatus.DefinitelyAssigned;
635
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
636
return DefiniteAssignmentStatus.DefinitelyAssigned;
637
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
638
return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
639
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
640
return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
642
return DefiniteAssignmentStatus.PotentiallyAssigned;
643
} else if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalOr) {
644
// C# 4.0 spec: §5.3.3.25 Definite Assignment for || expressions
645
bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left);
647
return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally
648
else if (cond == true)
649
return data; // right operand never gets evaluated
650
DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data);
651
DefiniteAssignmentStatus beforeRight;
652
if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
653
beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned;
654
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
655
beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned;
657
beforeRight = afterLeft;
658
DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight);
659
if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned)
660
return DefiniteAssignmentStatus.DefinitelyAssigned;
661
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
662
return DefiniteAssignmentStatus.DefinitelyAssigned;
663
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
664
return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
665
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
666
return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
668
return DefiniteAssignmentStatus.PotentiallyAssigned;
669
} else if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) {
670
// C# 4.0 spec: §5.3.3.27 Definite assignment for ?? expressions
671
ResolveResult crr = analysis.EvaluateConstant(binaryOperatorExpression.Left);
672
if (crr != null && crr.IsCompileTimeConstant && crr.ConstantValue == null)
673
return binaryOperatorExpression.Right.AcceptVisitor(this, data);
674
DefiniteAssignmentStatus status = CleanSpecialValues(binaryOperatorExpression.Left.AcceptVisitor(this, data));
675
binaryOperatorExpression.Right.AcceptVisitor(this, status);
678
// use default logic for other operators
679
return VisitChildren(binaryOperatorExpression, data);
683
public override DefiniteAssignmentStatus VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, DefiniteAssignmentStatus data)
685
if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) {
686
// C# 4.0 spec: §5.3.3.26 Definite assignment for ! expressions
687
DefiniteAssignmentStatus status = unaryOperatorExpression.Expression.AcceptVisitor(this, data);
688
if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
689
return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
690
else if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
691
return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
695
// use default logic for other operators
696
return VisitChildren(unaryOperatorExpression, data);
700
public override DefiniteAssignmentStatus VisitConditionalExpression(ConditionalExpression conditionalExpression, DefiniteAssignmentStatus data)
702
// C# 4.0 spec: §5.3.3.28 Definite assignment for ?: expressions
703
bool? cond = analysis.EvaluateCondition(conditionalExpression.Condition);
705
return conditionalExpression.TrueExpression.AcceptVisitor(this, data);
706
} else if (cond == false) {
707
return conditionalExpression.FalseExpression.AcceptVisitor(this, data);
709
DefiniteAssignmentStatus afterCondition = conditionalExpression.Condition.AcceptVisitor(this, data);
711
DefiniteAssignmentStatus beforeTrue, beforeFalse;
712
if (afterCondition == DefiniteAssignmentStatus.AssignedAfterTrueExpression) {
713
beforeTrue = DefiniteAssignmentStatus.DefinitelyAssigned;
714
beforeFalse = DefiniteAssignmentStatus.PotentiallyAssigned;
715
} else if (afterCondition == DefiniteAssignmentStatus.AssignedAfterFalseExpression) {
716
beforeTrue = DefiniteAssignmentStatus.PotentiallyAssigned;
717
beforeFalse = DefiniteAssignmentStatus.DefinitelyAssigned;
719
beforeTrue = afterCondition;
720
beforeFalse = afterCondition;
723
DefiniteAssignmentStatus afterTrue = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeTrue);
724
DefiniteAssignmentStatus afterFalse = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeFalse);
725
return MergeStatus(CleanSpecialValues(afterTrue), CleanSpecialValues(afterFalse));
729
public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data)
731
BlockStatement body = anonymousMethodExpression.Body;
732
analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
736
public override DefiniteAssignmentStatus VisitLambdaExpression(LambdaExpression lambdaExpression, DefiniteAssignmentStatus data)
738
Statement body = lambdaExpression.Body as Statement;
740
analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
742
lambdaExpression.Body.AcceptVisitor(this, data);
747
public override DefiniteAssignmentStatus VisitIdentifierExpression(IdentifierExpression identifierExpression, DefiniteAssignmentStatus data)
749
if (data != DefiniteAssignmentStatus.DefinitelyAssigned
750
&& identifierExpression.Identifier == analysis.variableName && identifierExpression.TypeArguments.Count == 0)
752
analysis.unassignedVariableUses.Add(identifierExpression);