Browse Source

Add definite assignment analysis.

pull/100/head
Daniel Grunwald 14 years ago
parent
commit
623f006ae4
  1. 283
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
  2. 579
      NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
  3. 7
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs
  4. 1
      NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

283
NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
namespace ICSharpCode.NRefactory.CSharp.Analysis
@ -16,27 +17,18 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -16,27 +17,18 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public readonly Statement PreviousStatement;
public readonly Statement NextStatement;
public ControlFlowNodeType Type;
public readonly ControlFlowNodeType Type;
public readonly List<ControlFlowNode> Predecessors = new List<ControlFlowNode>();
public readonly List<ControlFlowNode> Successors = new List<ControlFlowNode>();
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>();
public ControlFlowNode(Statement previousStatement, Statement nextStatement)
public ControlFlowNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
{
if (previousStatement == null && nextStatement == null)
throw new ArgumentException("previousStatement and nextStatement must not be both null");
this.PreviousStatement = previousStatement;
this.NextStatement = nextStatement;
}
/// <summary>
/// Creates a control flow edge from <c>source</c> to <c>this</c>.
/// </summary>
internal ControlFlowNode ConnectWith(ControlFlowNode source)
{
source.Successors.Add(this);
this.Predecessors.Add(source);
return this;
this.Type = type;
}
}
@ -64,74 +56,179 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -64,74 +56,179 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
LoopCondition
}
public class ControlFlowEdge
{
public readonly ControlFlowNode From;
public readonly ControlFlowNode To;
public readonly ControlFlowEdgeType Type;
List<TryCatchStatement> jumpOutOfTryFinally;
public ControlFlowEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type)
{
if (from == null)
throw new ArgumentNullException("from");
if (to == null)
throw new ArgumentNullException("to");
this.From = from;
this.To = to;
this.Type = type;
}
internal void AddJumpOutOfTryFinally(TryCatchStatement tryFinally)
{
if (jumpOutOfTryFinally == null)
jumpOutOfTryFinally = new List<TryCatchStatement>();
}
/// <summary>
/// Gets whether this control flow edge is leaving any try-finally statements.
/// </summary>
public bool IsLeavingTryFinally {
get { return jumpOutOfTryFinally != null; }
}
/// <summary>
/// Gets the try-finally statements that this control flow edge is leaving.
/// </summary>
public IEnumerable<TryCatchStatement> TryFinallyStatements {
get { return jumpOutOfTryFinally ?? Enumerable.Empty<TryCatchStatement>(); }
}
}
public enum ControlFlowEdgeType
{
/// <summary>
/// Regular control flow.
/// </summary>
Normal,
/// <summary>
/// Conditional control flow (edge taken if condition is true)
/// </summary>
ConditionTrue,
/// <summary>
/// Conditional control flow (edge taken if condition is false)
/// </summary>
ConditionFalse,
/// <summary>
/// A jump statement (goto, goto case, break or continue)
/// </summary>
Jump
}
/// <summary>
/// Constructs the control flow graph for C# statements.
/// </summary>
public class ControlFlowGraphBuilder
{
protected virtual ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement)
// Written according to the reachability rules in the C# spec (§8.1 End points and reachability)
protected virtual ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
{
return new ControlFlowNode(previousStatement, nextStatement);
return new ControlFlowNode(previousStatement, nextStatement, type);
}
protected virtual ControlFlowEdge CreateEdge(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type)
{
return new ControlFlowEdge(from, to, type);
}
Statement rootStatement;
List<ControlFlowNode> nodes;
Dictionary<string, ControlFlowNode> labels;
List<ControlFlowNode> gotoStatements;
public ControlFlowNode[] BuildControlFlowGraph(BlockStatement block)
public IList<ControlFlowNode> BuildControlFlowGraph(Statement statement)
{
NodeCreationVisitor nodeCreationVisitor = new NodeCreationVisitor();
nodeCreationVisitor.builder = this;
try {
nodes = new List<ControlFlowNode>();
labels = new Dictionary<string, ControlFlowNode>();
gotoStatements = new List<ControlFlowNode>();
ControlFlowNode entryPoint = CreateStartNode(block);
nodeCreationVisitor.VisitBlockStatement(block, entryPoint);
this.nodes = new List<ControlFlowNode>();
this.labels = new Dictionary<string, ControlFlowNode>();
this.gotoStatements = new List<ControlFlowNode>();
this.rootStatement = statement;
ControlFlowNode entryPoint = CreateStartNode(statement);
statement.AcceptVisitor(nodeCreationVisitor, entryPoint);
// Resolve goto statements:
foreach (ControlFlowNode gotoStmt in gotoStatements) {
string label = ((GotoStatement)gotoStmt.NextStatement).Label;
ControlFlowNode labelNode;
if (labels.TryGetValue(label, out labelNode))
labelNode.ConnectWith(gotoStmt);
nodeCreationVisitor.Connect(gotoStmt, labelNode, ControlFlowEdgeType.Jump);
}
return nodes.ToArray();
AnnotateLeaveEdgesWithTryFinallyBlocks();
return nodes;
} finally {
nodes = null;
labels = null;
gotoStatements = null;
this.nodes = null;
this.labels = null;
this.gotoStatements = null;
this.rootStatement = null;
}
}
void AnnotateLeaveEdgesWithTryFinallyBlocks()
{
foreach (ControlFlowEdge edge in nodes.SelectMany(n => n.Outgoing)) {
if (edge.Type != ControlFlowEdgeType.Jump) {
// Only jumps are potential candidates for leaving try-finally blocks.
// Note that the regular edges leaving try or catch blocks are already annotated by the visitor.
continue;
}
Statement gotoStatement = edge.From.NextStatement;
Debug.Assert(gotoStatement is GotoStatement || gotoStatement is GotoDefaultStatement || gotoStatement is GotoCaseStatement || gotoStatement is BreakStatement || gotoStatement is ContinueStatement);
Statement targetStatement = edge.To.PreviousStatement ?? edge.To.NextStatement;
if (gotoStatement.Parent == targetStatement.Parent)
continue;
HashSet<TryCatchStatement> targetParentTryCatch = new HashSet<TryCatchStatement>(targetStatement.Ancestors.OfType<TryCatchStatement>());
for (AstNode node = gotoStatement.Parent; node != null; node = node.Parent) {
TryCatchStatement leftTryCatch = node as TryCatchStatement;
if (leftTryCatch != null) {
if (targetParentTryCatch.Contains(leftTryCatch))
break;
if (!leftTryCatch.FinallyBlock.IsNull)
edge.AddJumpOutOfTryFinally(leftTryCatch);
}
}
}
}
#region Create*Node
ControlFlowNode CreateStartNode(Statement statement)
{
ControlFlowNode node = CreateNode(null, statement);
node.Type = ControlFlowNodeType.StartNode;
ControlFlowNode node = CreateNode(null, statement, ControlFlowNodeType.StartNode);
nodes.Add(node);
return node;
}
ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type)
{
ControlFlowNode node = CreateNode(statement, null);
node.Type = type;
ControlFlowNode node = CreateNode(statement, null, type);
nodes.Add(node);
return node;
}
ControlFlowNode CreateEndNode(Statement statement)
{
// Find the next statement in the same role:
AstNode next = statement;
do {
next = next.NextSibling;
} while (next != null && next.Role != statement.Role);
Statement nextStatement = next as Statement;
ControlFlowNode node = CreateNode(statement, nextStatement);
node.Type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode;
Statement nextStatement;
if (statement == rootStatement) {
nextStatement = null;
} else {
// Find the next statement in the same role:
AstNode next = statement;
do {
next = next.NextSibling;
} while (next != null && next.Role != statement.Role);
nextStatement = next as Statement;
}
ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode;
ControlFlowNode node = CreateNode(statement, nextStatement, type);
nodes.Add(node);
return node;
}
#endregion
#region Constant evaluation
/// <summary>
@ -172,6 +269,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -172,6 +269,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Stack<ControlFlowNode> continueTargets = new Stack<ControlFlowNode>();
List<ControlFlowNode> gotoCaseOrDefault = new List<ControlFlowNode>();
internal ControlFlowEdge Connect(ControlFlowNode from, ControlFlowNode to, ControlFlowEdgeType type = ControlFlowEdgeType.Normal)
{
ControlFlowEdge edge = builder.CreateEdge(from, to, type);
from.Outgoing.Add(edge);
to.Incoming.Add(edge);
return edge;
}
/// <summary>
/// Creates an end node for <c>stmt</c> and connects <c>from</c> with the new node.
/// </summary>
ControlFlowNode CreateConnectedEndNode(Statement stmt, ControlFlowNode from)
{
ControlFlowNode newNode = builder.CreateEndNode(stmt);
Connect(from, newNode);
return newNode;
}
protected override ControlFlowNode VisitChildren(AstNode node, ControlFlowNode data)
{
// We have overrides for all possible expressions and should visit expressions only.
@ -182,7 +297,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -182,7 +297,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
// C# 4.0 spec: §8.2 Blocks
ControlFlowNode childNode = HandleStatementList(blockStatement.Statements, data);
return builder.CreateEndNode(blockStatement).ConnectWith(childNode);
return CreateConnectedEndNode(blockStatement, childNode);
}
ControlFlowNode HandleStatementList(AstNodeCollection<Statement> statements, ControlFlowNode source)
@ -191,7 +306,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -191,7 +306,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
foreach (Statement stmt in statements) {
if (childNode == null) {
childNode = builder.CreateStartNode(stmt);
childNode.ConnectWith(source);
Connect(source, childNode);
}
Debug.Assert(childNode.NextStatement == stmt);
childNode = stmt.AcceptVisitor(this, childNode);
@ -202,24 +317,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -202,24 +317,24 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitEmptyStatement(EmptyStatement emptyStatement, ControlFlowNode data)
{
return builder.CreateEndNode(emptyStatement).ConnectWith(data);
return CreateConnectedEndNode(emptyStatement, data);
}
public override ControlFlowNode VisitLabelStatement(LabelStatement labelStatement, ControlFlowNode data)
{
ControlFlowNode end = builder.CreateEndNode(labelStatement);
ControlFlowNode end = CreateConnectedEndNode(labelStatement, data);
builder.labels[labelStatement.Label] = end;
return end.ConnectWith(data);
return end;
}
public override ControlFlowNode VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, ControlFlowNode data)
{
return builder.CreateEndNode(variableDeclarationStatement).ConnectWith(data);
return CreateConnectedEndNode(variableDeclarationStatement, data);
}
public override ControlFlowNode VisitExpressionStatement(ExpressionStatement expressionStatement, ControlFlowNode data)
{
return builder.CreateEndNode(expressionStatement).ConnectWith(data);
return CreateConnectedEndNode(expressionStatement, data);
}
public override ControlFlowNode VisitIfElseStatement(IfElseStatement ifElseStatement, ControlFlowNode data)
@ -227,7 +342,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -227,7 +342,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
bool? cond = builder.EvaluateCondition(ifElseStatement.Condition);
ControlFlowNode trueBegin = builder.CreateStartNode(ifElseStatement.TrueStatement);
if (cond != false)
trueBegin.ConnectWith(data);
Connect(data, trueBegin, ControlFlowEdgeType.ConditionTrue);
ControlFlowNode trueEnd = ifElseStatement.TrueStatement.AcceptVisitor(this, trueBegin);
ControlFlowNode falseEnd;
if (ifElseStatement.FalseStatement.IsNull) {
@ -235,15 +350,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -235,15 +350,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
} else {
ControlFlowNode falseBegin = builder.CreateStartNode(ifElseStatement.FalseStatement);
if (cond != true)
falseBegin.ConnectWith(data);
Connect(data, falseBegin, ControlFlowEdgeType.ConditionFalse);
falseEnd = ifElseStatement.FalseStatement.AcceptVisitor(this, falseBegin);
}
ControlFlowNode end = builder.CreateEndNode(ifElseStatement);
end.ConnectWith(trueEnd);
Connect(trueEnd, end);
if (falseEnd != null) {
end.ConnectWith(falseEnd);
Connect(falseEnd, end);
} else if (cond != true) {
end.ConnectWith(data);
Connect(data, end, ControlFlowEdgeType.ConditionFalse);
}
return end;
}
@ -283,7 +398,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -283,7 +398,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
breakTargets.Pop();
if (defaultSection == null && sectionMatchedByConstant == null) {
end.ConnectWith(data);
Connect(data, end);
}
if (gotoCaseOrDefault.Count > gotoCaseOrDefaultInOuterScope) {
@ -314,16 +429,16 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -314,16 +429,16 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Push(end);
continueTargets.Push(conditionNode);
conditionNode.ConnectWith(data);
Connect(data, conditionNode);
bool? cond = builder.EvaluateCondition(whileStatement.Condition);
ControlFlowNode bodyStart = builder.CreateStartNode(whileStatement.EmbeddedStatement);
if (cond != false)
bodyStart.ConnectWith(conditionNode);
Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue);
ControlFlowNode bodyEnd = whileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart);
conditionNode.ConnectWith(bodyEnd);
Connect(bodyEnd, conditionNode);
if (cond != true)
end.ConnectWith(conditionNode);
Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
breakTargets.Pop();
continueTargets.Pop();
@ -339,15 +454,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -339,15 +454,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
continueTargets.Push(conditionNode);
ControlFlowNode bodyStart = builder.CreateStartNode(doWhileStatement.EmbeddedStatement);
bodyStart.ConnectWith(data);
Connect(data, bodyStart);
ControlFlowNode bodyEnd = doWhileStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart);
conditionNode.ConnectWith(bodyEnd);
Connect(bodyEnd, conditionNode);
bool? cond = builder.EvaluateCondition(doWhileStatement.Condition);
if (cond != false)
bodyStart.ConnectWith(conditionNode);
Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue);
if (cond != true)
end.ConnectWith(conditionNode);
Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
breakTargets.Pop();
continueTargets.Pop();
@ -360,14 +475,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -360,14 +475,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
// for (initializers <data>; <condition>cond; <iteratorStart>iterators<iteratorEnd>) { <bodyStart> embeddedStmt; <bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(forStatement);
ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition);
conditionNode.ConnectWith(data);
Connect(data, conditionNode);
int iteratorStartNodeID = builder.nodes.Count;
ControlFlowNode iteratorEnd = HandleStatementList(forStatement.Iterators, null);
ControlFlowNode iteratorStart;
if (iteratorEnd != null) {
iteratorStart = builder.nodes[iteratorStartNodeID];
iteratorEnd.ConnectWith(conditionNode);
Connect(iteratorEnd, conditionNode);
} else {
iteratorStart = conditionNode;
}
@ -377,16 +492,16 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -377,16 +492,16 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
ControlFlowNode bodyStart = builder.CreateStartNode(forStatement.EmbeddedStatement);
ControlFlowNode bodyEnd = forStatement.EmbeddedStatement.AcceptVisitor(this, bodyStart);
iteratorStart.ConnectWith(bodyEnd);
Connect(bodyEnd, iteratorStart);
breakTargets.Pop();
continueTargets.Pop();
bool? cond = forStatement.Condition.IsNull ? true : builder.EvaluateCondition(forStatement.Condition);
if (cond != false)
bodyStart.ConnectWith(conditionNode);
Connect(conditionNode, bodyStart, ControlFlowEdgeType.ConditionTrue);
if (cond != true)
end.ConnectWith(conditionNode);
Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
return end;
}
@ -397,7 +512,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -397,7 +512,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return source;
ControlFlowNode bodyStart = builder.CreateStartNode(embeddedStatement);
if (source != null)
bodyStart.ConnectWith(source);
Connect(source, bodyStart);
return embeddedStatement.AcceptVisitor(this, bodyStart);
}
@ -406,31 +521,33 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -406,31 +521,33 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
// <data> foreach (<condition>...) { <bodyStart>embeddedStmt<bodyEnd> } <end>
ControlFlowNode end = builder.CreateEndNode(foreachStatement);
ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition);
conditionNode.ConnectWith(data);
Connect(data, conditionNode);
breakTargets.Push(end);
continueTargets.Push(conditionNode);
ControlFlowNode bodyEnd = HandleEmbeddedStatement(foreachStatement.EmbeddedStatement, conditionNode);
conditionNode.ConnectWith(bodyEnd);
Connect(bodyEnd, conditionNode);
breakTargets.Pop();
continueTargets.Pop();
return end.ConnectWith(conditionNode);
Connect(conditionNode, end);
return end;
}
public override ControlFlowNode VisitBreakStatement(BreakStatement breakStatement, ControlFlowNode data)
{
if (breakTargets.Count > 0)
breakTargets.Peek().ConnectWith(data);
Connect(data, breakTargets.Peek(), ControlFlowEdgeType.Jump);
return builder.CreateEndNode(breakStatement);
}
public override ControlFlowNode VisitContinueStatement(ContinueStatement continueStatement, ControlFlowNode data)
{
if (continueTargets.Count > 0)
continueTargets.Peek().ConnectWith(data);
Connect(data, continueTargets.Peek(), ControlFlowEdgeType.Jump);
return builder.CreateEndNode(continueStatement);
}
@ -453,9 +570,13 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -453,9 +570,13 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data)
{
ControlFlowNode end = builder.CreateEndNode(tryCatchStatement);
end.ConnectWith(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data));
var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end);
if (!tryCatchStatement.FinallyBlock.IsNull)
edge.AddJumpOutOfTryFinally(tryCatchStatement);
foreach (CatchClause cc in tryCatchStatement.CatchClauses) {
end.ConnectWith(HandleEmbeddedStatement(cc.Body, data));
edge = Connect(HandleEmbeddedStatement(cc.Body, data), end);
if (!tryCatchStatement.FinallyBlock.IsNull)
edge.AddJumpOutOfTryFinally(tryCatchStatement);
}
if (!tryCatchStatement.FinallyBlock.IsNull) {
// Don't connect the end of the try-finally block to anything.
@ -468,31 +589,31 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -468,31 +589,31 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitCheckedStatement(CheckedStatement checkedStatement, ControlFlowNode data)
{
ControlFlowNode bodyEnd = HandleEmbeddedStatement(checkedStatement.Body, data);
return builder.CreateEndNode(checkedStatement).ConnectWith(bodyEnd);
return CreateConnectedEndNode(checkedStatement, bodyEnd);
}
public override ControlFlowNode VisitUncheckedStatement(UncheckedStatement uncheckedStatement, ControlFlowNode data)
{
ControlFlowNode bodyEnd = HandleEmbeddedStatement(uncheckedStatement.Body, data);
return builder.CreateEndNode(uncheckedStatement).ConnectWith(bodyEnd);
return CreateConnectedEndNode(uncheckedStatement, bodyEnd);
}
public override ControlFlowNode VisitLockStatement(LockStatement lockStatement, ControlFlowNode data)
{
ControlFlowNode bodyEnd = HandleEmbeddedStatement(lockStatement.EmbeddedStatement, data);
return builder.CreateEndNode(lockStatement).ConnectWith(bodyEnd);
return CreateConnectedEndNode(lockStatement, bodyEnd);
}
public override ControlFlowNode VisitUsingStatement(UsingStatement usingStatement, ControlFlowNode data)
{
data = HandleEmbeddedStatement(usingStatement.ResourceAcquisition as Statement, data);
ControlFlowNode bodyEnd = HandleEmbeddedStatement(usingStatement.EmbeddedStatement, data);
return builder.CreateEndNode(usingStatement).ConnectWith(bodyEnd);
return CreateConnectedEndNode(usingStatement, bodyEnd);
}
public override ControlFlowNode VisitYieldStatement(YieldStatement yieldStatement, ControlFlowNode data)
{
return builder.CreateEndNode(yieldStatement).ConnectWith(data);
return CreateConnectedEndNode(yieldStatement, data);
}
public override ControlFlowNode VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, ControlFlowNode data)
@ -503,13 +624,13 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis @@ -503,13 +624,13 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitUnsafeStatement(UnsafeStatement unsafeStatement, ControlFlowNode data)
{
ControlFlowNode bodyEnd = HandleEmbeddedStatement(unsafeStatement.Body, data);
return builder.CreateEndNode(unsafeStatement).ConnectWith(bodyEnd);
return CreateConnectedEndNode(unsafeStatement, bodyEnd);
}
public override ControlFlowNode VisitFixedStatement(FixedStatement fixedStatement, ControlFlowNode data)
{
ControlFlowNode bodyEnd = HandleEmbeddedStatement(fixedStatement.EmbeddedStatement, data);
return builder.CreateEndNode(fixedStatement).ConnectWith(bodyEnd);
return CreateConnectedEndNode(fixedStatement, bodyEnd);
}
}
}

579
NRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs

@ -0,0 +1,579 @@ @@ -0,0 +1,579 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
namespace ICSharpCode.NRefactory.CSharp.Analysis
{
/// <summary>
/// Represents the definite assignment status of a variable at a specific location.
/// </summary>
public enum DefiniteAssignmentStatus
{
/// <summary>
/// The variable might be assigned or unassigned.
/// </summary>
PotentiallyAssigned,
/// <summary>
/// The variable is definitely unassigned.
/// </summary>
DefinitelyUnassigned,
/// <summary>
/// The variable is definitely assigned.
/// </summary>
DefinitelyAssigned,
/// <summary>
/// The variable is definitely assigned iff the expression results in the value 'true'.
/// </summary>
AssignedAfterTrueExpression,
/// <summary>
/// The variable is definitely assigned iff the expression results in the value 'false'.
/// </summary>
AssignedAfterFalseExpression,
/// <summary>
/// The code is unreachable.
/// </summary>
CodeUnreachable
}
/// <summary>
/// Implements the C# definite assignment analysis (C# 4.0 Spec: §5.3 Definite assignment)
/// </summary>
public class DefiniteAssignmentAnalysis
{
readonly DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor();
readonly List<ControlFlowNode> allNodes = new List<ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> beginNodeDict = new Dictionary<Statement, ControlFlowNode>();
readonly Dictionary<Statement, ControlFlowNode> endNodeDict = new Dictionary<Statement, ControlFlowNode>();
Dictionary<ControlFlowNode, DefiniteAssignmentStatus> nodeStatus = new Dictionary<ControlFlowNode, DefiniteAssignmentStatus>();
Dictionary<ControlFlowEdge, DefiniteAssignmentStatus> edgeStatus = new Dictionary<ControlFlowEdge, DefiniteAssignmentStatus>();
string variableName;
List<IdentifierExpression> unassignedVariableUses = new List<IdentifierExpression>();
Queue<ControlFlowNode> nodesWithModifiedInput = new Queue<ControlFlowNode>();
public DefiniteAssignmentAnalysis(Statement rootStatement)
{
visitor.analysis = this;
ControlFlowGraphBuilder b = new ControlFlowGraphBuilder();
allNodes.AddRange(b.BuildControlFlowGraph(rootStatement));
foreach (AstNode descendant in rootStatement.Descendants) {
// Anonymous methods have separate control flow graphs, but we also need to analyze those.
AnonymousMethodExpression ame = descendant as AnonymousMethodExpression;
if (ame != null)
allNodes.AddRange(b.BuildControlFlowGraph(ame.Body));
LambdaExpression lambda = descendant as LambdaExpression;
if (lambda != null && lambda.Body is Statement)
allNodes.AddRange(b.BuildControlFlowGraph((Statement)lambda.Body));
}
// Verify that we created nodes for all statements:
Debug.Assert(!rootStatement.DescendantsAndSelf.OfType<Statement>().Except(allNodes.Select(n => n.NextStatement)).Any());
// Now register the nodes in the dictionaries:
foreach (ControlFlowNode node in allNodes) {
if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements)
beginNodeDict.Add(node.NextStatement, node);
if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode)
endNodeDict.Add(node.PreviousStatement, node);
}
}
public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.DefinitelyUnassigned)
{
this.variableName = variable;
// Reset the status:
unassignedVariableUses.Clear();
foreach (ControlFlowNode node in allNodes) {
nodeStatus[node] = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Outgoing)
edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable;
}
ChangeNodeStatus(allNodes[0], DefiniteAssignmentStatus.DefinitelyUnassigned);
// Iterate as long as the input status of some nodes is changing:
while (nodesWithModifiedInput.Count > 0) {
ControlFlowNode node = nodesWithModifiedInput.Dequeue();
DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Incoming) {
inputStatus = MergeStatus(inputStatus, edgeStatus[edge]);
}
ChangeNodeStatus(node, inputStatus);
}
}
static DefiniteAssignmentStatus MergeStatus(DefiniteAssignmentStatus a, DefiniteAssignmentStatus b)
{
// The result will be DefinitelyAssigned if at least one incoming edge is DefinitelyAssigned and all others are unreachable.
// The result will be DefinitelyUnassigned if at least one incoming edge is DefinitelyUnassigned and all others are unreachable.
// The result will be Unreachable if all incoming edges are unreachable.
// Otherwise, the result will be PotentiallyAssigned.
if (a == b)
return a;
else if (a == DefiniteAssignmentStatus.CodeUnreachable)
return b;
else if (b == DefiniteAssignmentStatus.CodeUnreachable)
return a;
else
return DefiniteAssignmentStatus.PotentiallyAssigned;
}
void ChangeNodeStatus(ControlFlowNode node, DefiniteAssignmentStatus inputStatus)
{
if (nodeStatus[node] == inputStatus)
return;
nodeStatus[node] = inputStatus;
DefiniteAssignmentStatus outputStatus;
switch (node.Type) {
case ControlFlowNodeType.StartNode:
case ControlFlowNodeType.BetweenStatements:
if (node.NextStatement is IfElseStatement) {
// Handle if-else as a condition node
goto case ControlFlowNodeType.LoopCondition;
}
if (inputStatus == DefiniteAssignmentStatus.DefinitelyAssigned) {
// There isn't any way to un-assign variables, so we don't have to check the expression
// if the status already is definitely assigned.
outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned;
} else {
outputStatus = CleanSpecialValues(node.NextStatement.AcceptVisitor(visitor, inputStatus));
}
break;
case ControlFlowNodeType.EndNode:
outputStatus = inputStatus;
if (node.PreviousStatement.Role == TryCatchStatement.FinallyBlockRole
&& (outputStatus == DefiniteAssignmentStatus.DefinitelyAssigned || outputStatus == DefiniteAssignmentStatus.PotentiallyAssigned))
{
TryCatchStatement tryFinally = (TryCatchStatement)node.PreviousStatement.Parent;
// Changing the status on a finally block potentially changes the status of all edges leaving that finally block:
foreach (ControlFlowEdge edge in allNodes.SelectMany(n => n.Outgoing)) {
DefiniteAssignmentStatus s = edgeStatus[edge];
if (s == DefiniteAssignmentStatus.DefinitelyUnassigned || s == DefiniteAssignmentStatus.PotentiallyAssigned) {
ChangeEdgeStatus(edge, outputStatus);
}
}
}
break;
case ControlFlowNodeType.LoopCondition:
ForeachStatement foreachStmt = node.NextStatement as ForeachStatement;
if (foreachStmt != null) {
outputStatus = CleanSpecialValues(foreachStmt.InExpression.AcceptVisitor(visitor, inputStatus));
if (foreachStmt.VariableName == this.variableName)
outputStatus = DefiniteAssignmentStatus.DefinitelyAssigned;
break;
} else {
Debug.Assert(node.NextStatement is IfElseStatement || node.NextStatement is WhileStatement || node.NextStatement is ForStatement || node.NextStatement is DoWhileStatement);
Expression condition = node.NextStatement.GetChildByRole(AstNode.Roles.Condition);
if (condition.IsNull)
outputStatus = inputStatus;
else
outputStatus = condition.AcceptVisitor(visitor, inputStatus);
foreach (ControlFlowEdge edge in node.Outgoing) {
if (edge.Type == ControlFlowEdgeType.ConditionTrue && outputStatus == DefiniteAssignmentStatus.AssignedAfterTrueExpression) {
ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned);
} else if (edge.Type == ControlFlowEdgeType.ConditionFalse && outputStatus == DefiniteAssignmentStatus.AssignedAfterFalseExpression) {
ChangeEdgeStatus(edge, DefiniteAssignmentStatus.DefinitelyAssigned);
} else {
ChangeEdgeStatus(edge, CleanSpecialValues(outputStatus));
}
}
return;
}
default:
throw new InvalidOperationException();
}
foreach (ControlFlowEdge edge in node.Outgoing) {
ChangeEdgeStatus(edge, outputStatus);
}
}
void ChangeEdgeStatus(ControlFlowEdge edge, DefiniteAssignmentStatus newStatus)
{
DefiniteAssignmentStatus oldStatus = edgeStatus[edge];
if (oldStatus == newStatus)
return;
// Ensure that status can change only in one direction:
// CodeUnreachable -> PotentiallyAssigned -> Definitely[Un]Assigned
// Going against this direction indicates a bug and could cause infinite loops.
switch (newStatus) {
case DefiniteAssignmentStatus.PotentiallyAssigned:
if (oldStatus != DefiniteAssignmentStatus.CodeUnreachable)
throw new InvalidOperationException("Invalid state transition");
break;
case DefiniteAssignmentStatus.DefinitelyUnassigned:
case DefiniteAssignmentStatus.DefinitelyAssigned:
if (!(oldStatus == DefiniteAssignmentStatus.CodeUnreachable || oldStatus == DefiniteAssignmentStatus.PotentiallyAssigned))
throw new InvalidOperationException("Invalid state transition");
break;
case DefiniteAssignmentStatus.CodeUnreachable:
throw new InvalidOperationException("Invalid state transition");
default:
throw new InvalidOperationException("Invalid value for DefiniteAssignmentStatus");
}
edgeStatus[edge] = newStatus;
nodesWithModifiedInput.Enqueue(edge.To);
}
/// <summary>
/// Evaluates an expression.
/// </summary>
/// <returns>The constant value of the expression; or null if the expression is not a constant.</returns>
ConstantResolveResult EvaluateConstant(Expression expr)
{
return null; // TODO: implement this using the C# resolver
}
/// <summary>
/// Evaluates an expression.
/// </summary>
/// <returns>The value of the constant boolean expression; or null if the value is not a constant boolean expression.</returns>
bool? EvaluateCondition(Expression expr)
{
ConstantResolveResult crr = EvaluateConstant(expr);
if (crr != null)
return crr.ConstantValue as bool?;
else
return null;
}
static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status)
{
if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.PotentiallyAssigned;
else if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.PotentiallyAssigned;
else
return status;
}
sealed class DefiniteAssignmentVisitor : DepthFirstAstVisitor<DefiniteAssignmentStatus, DefiniteAssignmentStatus>
{
internal DefiniteAssignmentAnalysis analysis;
// The general approach for unknown nodes is to pass the status through all child nodes in order
protected override DefiniteAssignmentStatus VisitChildren(AstNode node, DefiniteAssignmentStatus data)
{
// the special values are valid as output only, not as input
Debug.Assert(data == CleanSpecialValues(data));
DefiniteAssignmentStatus status = data;
foreach (AstNode child in node.Children) {
Debug.Assert(!(child is Statement)); // statements are visited with the CFG, not with the visitor pattern
status = child.AcceptVisitor(this, status);
status = CleanSpecialValues(status);
}
return status;
}
#region Statements
// For statements, the visitor only describes the effect of the statement itself;
// we do not consider the effect of any nested statements.
// This is done because the nested statements will be reached using the control flow graph.
// In fact, these methods are present so that the default logic in VisitChildren does not try to visit the nested statements.
public override DefiniteAssignmentStatus VisitBlockStatement(BlockStatement blockStatement, DefiniteAssignmentStatus data)
{
return data;
}
public override DefiniteAssignmentStatus VisitCheckedStatement(CheckedStatement checkedStatement, DefiniteAssignmentStatus data)
{
return data;
}
public override DefiniteAssignmentStatus VisitUncheckedStatement(UncheckedStatement uncheckedStatement, DefiniteAssignmentStatus data)
{
return data;
}
// ExpressionStatement handled by default logic
// VariableDeclarationStatement handled by default logic
public override DefiniteAssignmentStatus VisitVariableInitializer(VariableInitializer variableInitializer, DefiniteAssignmentStatus data)
{
if (variableInitializer.Initializer.IsNull) {
return data;
} else {
DefiniteAssignmentStatus status = variableInitializer.Initializer.AcceptVisitor(this, data);
if (variableInitializer.Name == analysis.variableName)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else
return status;
}
}
// IfStatement not handled by visitor, but special-cased in the code consuming the control flow graph
public override DefiniteAssignmentStatus VisitSwitchStatement(SwitchStatement switchStatement, DefiniteAssignmentStatus data)
{
return switchStatement.Expression.AcceptVisitor(this, data);
}
public override DefiniteAssignmentStatus VisitWhileStatement(WhileStatement whileStatement, DefiniteAssignmentStatus data)
{
return data; // condition is handled by special condition CFG node
}
public override DefiniteAssignmentStatus VisitDoWhileStatement(DoWhileStatement doWhileStatement, DefiniteAssignmentStatus data)
{
return data; // condition is handled by special condition CFG node
}
public override DefiniteAssignmentStatus VisitForStatement(ForStatement forStatement, DefiniteAssignmentStatus data)
{
return data; // condition is handled by special condition CFG node; initializer and iterator statements are handled by CFG
}
// Break/Continue/Goto: handled by default logic
// ThrowStatement: handled by default logic (just visit the expression)
// ReturnStatement: handled by default logic (just visit the expression)
public override DefiniteAssignmentStatus VisitTryCatchStatement(TryCatchStatement tryCatchStatement, DefiniteAssignmentStatus data)
{
return data; // no special logic when entering the try-catch-finally statement
// TODO: where to put the special logic when exiting the try-finally statement?
}
public override DefiniteAssignmentStatus VisitForeachStatement(ForeachStatement foreachStatement, DefiniteAssignmentStatus data)
{
return data; // assignment of the foreach loop variable is done when handling the condition node
}
public override DefiniteAssignmentStatus VisitUsingStatement(UsingStatement usingStatement, DefiniteAssignmentStatus data)
{
if (usingStatement.ResourceAcquisition is Expression)
return usingStatement.ResourceAcquisition.AcceptVisitor(this, data);
else
return data; // don't handle resource acquisition statements, as those are connected in the control flow graph
}
public override DefiniteAssignmentStatus VisitLockStatement(LockStatement lockStatement, DefiniteAssignmentStatus data)
{
return lockStatement.Expression.AcceptVisitor(this, data);
}
// Yield statements use the default logic
public override DefiniteAssignmentStatus VisitUnsafeStatement(UnsafeStatement unsafeStatement, DefiniteAssignmentStatus data)
{
return data;
}
public override DefiniteAssignmentStatus VisitFixedStatement(FixedStatement fixedStatement, DefiniteAssignmentStatus data)
{
DefiniteAssignmentStatus status = data;
foreach (var variable in fixedStatement.Variables)
status = variable.AcceptVisitor(this, status);
return status;
}
#endregion
public override DefiniteAssignmentStatus VisitDirectionExpression(DirectionExpression directionExpression, DefiniteAssignmentStatus data)
{
if (directionExpression.FieldDirection == FieldDirection.Out) {
return HandleAssignment(directionExpression.Expression, null, data);
} else {
// use default logic for 'ref'
return VisitChildren(directionExpression, data);
}
}
public override DefiniteAssignmentStatus VisitAssignmentExpression(AssignmentExpression assignmentExpression, DefiniteAssignmentStatus data)
{
if (assignmentExpression.Operator == AssignmentOperatorType.Assign) {
return HandleAssignment(assignmentExpression.Left, assignmentExpression.Right, data);
} else {
// use default logic for compound assignment operators
return VisitChildren(assignmentExpression, data);
}
}
DefiniteAssignmentStatus HandleAssignment(Expression left, Expression right, DefiniteAssignmentStatus initialStatus)
{
IdentifierExpression ident = left as IdentifierExpression;
if (ident != null && ident.Identifier == analysis.variableName) {
right.AcceptVisitor(this, initialStatus);
return DefiniteAssignmentStatus.DefinitelyAssigned;
} else {
DefiniteAssignmentStatus status = left.AcceptVisitor(this, initialStatus);
status = right.AcceptVisitor(this, CleanSpecialValues(status));
return CleanSpecialValues(status);
}
}
public override DefiniteAssignmentStatus VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, DefiniteAssignmentStatus data)
{
// Don't use the default logic here because we don't want to clean up the special values.
return parenthesizedExpression.Expression.AcceptVisitor(this, data);
}
public override DefiniteAssignmentStatus VisitCheckedExpression(CheckedExpression checkedExpression, DefiniteAssignmentStatus data)
{
return checkedExpression.Expression.AcceptVisitor(this, data);
}
public override DefiniteAssignmentStatus VisitUncheckedExpression(UncheckedExpression uncheckedExpression, DefiniteAssignmentStatus data)
{
return uncheckedExpression.Expression.AcceptVisitor(this, data);
}
public override DefiniteAssignmentStatus VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, DefiniteAssignmentStatus data)
{
if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalAnd) {
// Handle constant left side of && expressions (not in the C# spec, but done by the MS compiler)
bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left);
if (cond == true)
return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally
else if (cond == false)
return data; // right operand never gets evaluated
// C# 4.0 spec: §5.3.3.24 Definite Assignment for && expressions
DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data);
DefiniteAssignmentStatus beforeRight;
if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned;
else
beforeRight = afterLeft;
DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight);
if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyUnassigned)
return afterRight;
else
return DefiniteAssignmentStatus.PotentiallyAssigned;
} else if (binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalOr) {
// C# 4.0 spec: §5.3.3.25 Definite Assignment for || expressions
bool? cond = analysis.EvaluateCondition(binaryOperatorExpression.Left);
if (cond == false)
return binaryOperatorExpression.Right.AcceptVisitor(this, data); // right operand gets evaluated unconditionally
else if (cond == true)
return data; // right operand never gets evaluated
DefiniteAssignmentStatus afterLeft = binaryOperatorExpression.Left.AcceptVisitor(this, data);
DefiniteAssignmentStatus beforeRight;
if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
beforeRight = DefiniteAssignmentStatus.PotentiallyAssigned;
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
beforeRight = DefiniteAssignmentStatus.DefinitelyAssigned;
else
beforeRight = afterLeft;
DefiniteAssignmentStatus afterRight = binaryOperatorExpression.Right.AcceptVisitor(this, beforeRight);
if (afterLeft == DefiniteAssignmentStatus.DefinitelyAssigned)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned && afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.DefinitelyAssigned;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyAssigned || afterRight == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
else if (afterLeft == DefiniteAssignmentStatus.AssignedAfterTrueExpression && afterRight == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
else if (afterRight == DefiniteAssignmentStatus.DefinitelyUnassigned)
return DefiniteAssignmentStatus.DefinitelyUnassigned;
else
return DefiniteAssignmentStatus.PotentiallyAssigned;
} else if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) {
// C# 4.0 spec: §5.3.3.27 Definite assignment for ?? expressions
ConstantResolveResult crr = analysis.EvaluateConstant(binaryOperatorExpression.Left);
if (crr != null && crr.ConstantValue == null)
return binaryOperatorExpression.Right.AcceptVisitor(this, data);
DefiniteAssignmentStatus status = CleanSpecialValues(binaryOperatorExpression.Left.AcceptVisitor(this, data));
binaryOperatorExpression.Right.AcceptVisitor(this, status);
return status;
} else {
// use default logic for other operators
return VisitChildren(binaryOperatorExpression, data);
}
}
public override DefiniteAssignmentStatus VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, DefiniteAssignmentStatus data)
{
if (unaryOperatorExpression.Operator == UnaryOperatorType.Not) {
// C# 4.0 spec: §5.3.3.26 Definite assignment for ! expressions
DefiniteAssignmentStatus status = unaryOperatorExpression.Expression.AcceptVisitor(this, data);
if (status == DefiniteAssignmentStatus.AssignedAfterFalseExpression)
return DefiniteAssignmentStatus.AssignedAfterTrueExpression;
else if (status == DefiniteAssignmentStatus.AssignedAfterTrueExpression)
return DefiniteAssignmentStatus.AssignedAfterFalseExpression;
else
return status;
} else {
// use default logic for other operators
return VisitChildren(unaryOperatorExpression, data);
}
}
public override DefiniteAssignmentStatus VisitConditionalExpression(ConditionalExpression conditionalExpression, DefiniteAssignmentStatus data)
{
// C# 4.0 spec: §5.3.3.28 Definite assignment for ?: expressions
bool? cond = analysis.EvaluateCondition(conditionalExpression.Condition);
if (cond == true) {
return conditionalExpression.TrueExpression.AcceptVisitor(this, data);
} else if (cond == false) {
return conditionalExpression.FalseExpression.AcceptVisitor(this, data);
} else {
DefiniteAssignmentStatus afterCondition = conditionalExpression.Condition.AcceptVisitor(this, data);
DefiniteAssignmentStatus beforeTrue, beforeFalse;
if (afterCondition == DefiniteAssignmentStatus.AssignedAfterTrueExpression) {
beforeTrue = DefiniteAssignmentStatus.DefinitelyAssigned;
beforeFalse = DefiniteAssignmentStatus.PotentiallyAssigned;
} else if (afterCondition == DefiniteAssignmentStatus.AssignedAfterFalseExpression) {
beforeTrue = DefiniteAssignmentStatus.PotentiallyAssigned;
beforeFalse = DefiniteAssignmentStatus.DefinitelyAssigned;
} else {
beforeTrue = afterCondition;
beforeFalse = afterCondition;
}
DefiniteAssignmentStatus afterTrue = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeTrue);
DefiniteAssignmentStatus afterFalse = conditionalExpression.TrueExpression.AcceptVisitor(this, beforeFalse);
return MergeStatus(CleanSpecialValues(afterTrue), CleanSpecialValues(afterFalse));
}
}
public override DefiniteAssignmentStatus VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, DefiniteAssignmentStatus data)
{
BlockStatement body = anonymousMethodExpression.Body;
foreach (ControlFlowNode node in analysis.allNodes) {
if (node.NextStatement == body)
analysis.ChangeNodeStatus(node, data);
}
return data;
}
public override DefiniteAssignmentStatus VisitLambdaExpression(LambdaExpression lambdaExpression, DefiniteAssignmentStatus data)
{
Statement body = lambdaExpression.Body as Statement;
if (body != null) {
foreach (ControlFlowNode node in analysis.allNodes) {
if (node.NextStatement == body)
analysis.ChangeNodeStatus(node, data);
}
} else {
lambdaExpression.Body.AcceptVisitor(this, data);
}
return data;
}
public override DefiniteAssignmentStatus VisitIdentifierExpression(IdentifierExpression identifierExpression, DefiniteAssignmentStatus data)
{
if (data != DefiniteAssignmentStatus.DefinitelyAssigned
&& identifierExpression.Identifier == analysis.variableName && identifierExpression.TypeArguments.Count == 0)
{
analysis.unassignedVariableUses.Add(identifierExpression);
}
return data;
}
}
}
}

7
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs

@ -30,10 +30,11 @@ namespace ICSharpCode.NRefactory.CSharp @@ -30,10 +30,11 @@ namespace ICSharpCode.NRefactory.CSharp
public int Count {
get {
var e = GetEnumerator();
int count = 0;
while (e.MoveNext())
count++;
for (AstNode cur = node.FirstChild; cur != null; cur = cur.NextSibling) {
if (cur.Role == role)
count++;
}
return count;
}
}

1
NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -62,6 +62,7 @@ @@ -62,6 +62,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CSharp\Analysis\ControlFlow.cs" />
<Compile Include="CSharp\Analysis\DefiniteAssignmentAnalysis.cs" />
<Compile Include="CSharp\Ast\AstNodeCollection.cs" />
<Compile Include="CSharp\Ast\Expressions\TypeReferenceExpression.cs" />
<Compile Include="CSharp\Ast\IAstVisitor.cs" />

Loading…
Cancel
Save