diff --git a/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs b/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
index 92ffa63fa7..1660e99b14 100644
--- a/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
+++ b/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
@@ -222,14 +222,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return node;
}
- ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type)
+ ControlFlowNode CreateSpecialNode(Statement statement, ControlFlowNodeType type, bool addToNodeList = true)
{
ControlFlowNode node = CreateNode(null, statement, type);
- nodes.Add(node);
+ if (addToNodeList)
+ nodes.Add(node);
return node;
}
- ControlFlowNode CreateEndNode(Statement statement)
+ ControlFlowNode CreateEndNode(Statement statement, bool addToNodeList = true)
{
Statement nextStatement;
if (statement == rootStatement) {
@@ -244,7 +245,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
ControlFlowNodeType type = nextStatement != null ? ControlFlowNodeType.BetweenStatements : ControlFlowNodeType.EndNode;
ControlFlowNode node = CreateNode(statement, nextStatement, type);
- nodes.Add(node);
+ if (addToNodeList)
+ nodes.Add(node);
return node;
}
#endregion
@@ -259,7 +261,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// Evaluates an expression.
///
/// The constant value of the expression; or null if the expression is not a constant.
- internal ConstantResolveResult EvaluateConstant(Expression expr)
+ ConstantResolveResult EvaluateConstant(Expression expr)
{
if (EvaluateOnlyPrimitiveConstants) {
if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
@@ -272,7 +274,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// Evaluates an expression.
///
/// The value of the constant boolean expression; or null if the value is not a constant boolean expression.
- internal bool? EvaluateCondition(Expression expr)
+ bool? EvaluateCondition(Expression expr)
{
ConstantResolveResult rr = EvaluateConstant(expr);
if (rr != null)
@@ -418,7 +420,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
int gotoCaseOrDefaultInOuterScope = gotoCaseOrDefault.Count;
- ControlFlowNode end = builder.CreateEndNode(switchStatement);
+ ControlFlowNode end = builder.CreateEndNode(switchStatement, addToNodeList: false);
breakTargets.Push(end);
foreach (SwitchSection section in switchStatement.SwitchSections) {
if (constant == null || section == sectionMatchedByConstant) {
@@ -439,6 +441,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
throw new NotImplementedException();
}
+ builder.nodes.Add(end);
return end;
}
@@ -457,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitWhileStatement(WhileStatement whileStatement, ControlFlowNode data)
{
// while (cond) { embeddedStmt; }
- ControlFlowNode end = builder.CreateEndNode(whileStatement);
+ ControlFlowNode end = builder.CreateEndNode(whileStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(whileStatement, ControlFlowNodeType.LoopCondition);
breakTargets.Push(end);
continueTargets.Push(conditionNode);
@@ -475,14 +478,15 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Pop();
continueTargets.Pop();
+ builder.nodes.Add(end);
return end;
}
public override ControlFlowNode VisitDoWhileStatement(DoWhileStatement doWhileStatement, ControlFlowNode data)
{
// do { embeddedStmt; } while(cond);
- ControlFlowNode end = builder.CreateEndNode(doWhileStatement);
- ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition);
+ ControlFlowNode end = builder.CreateEndNode(doWhileStatement, addToNodeList: false);
+ ControlFlowNode conditionNode = builder.CreateSpecialNode(doWhileStatement, ControlFlowNodeType.LoopCondition, addToNodeList: false);
breakTargets.Push(end);
continueTargets.Push(conditionNode);
@@ -499,6 +503,8 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
breakTargets.Pop();
continueTargets.Pop();
+ builder.nodes.Add(conditionNode);
+ builder.nodes.Add(end);
return end;
}
@@ -506,7 +512,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
data = HandleStatementList(forStatement.Initializers, data);
// for (initializers ; cond; iterators) { embeddedStmt; }
- ControlFlowNode end = builder.CreateEndNode(forStatement);
+ ControlFlowNode end = builder.CreateEndNode(forStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(forStatement, ControlFlowNodeType.LoopCondition);
Connect(data, conditionNode);
@@ -536,6 +542,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (cond != true)
Connect(conditionNode, end, ControlFlowEdgeType.ConditionFalse);
+ builder.nodes.Add(end);
return end;
}
@@ -552,7 +559,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitForeachStatement(ForeachStatement foreachStatement, ControlFlowNode data)
{
// foreach (...) { embeddedStmt }
- ControlFlowNode end = builder.CreateEndNode(foreachStatement);
+ ControlFlowNode end = builder.CreateEndNode(foreachStatement, addToNodeList: false);
ControlFlowNode conditionNode = builder.CreateSpecialNode(foreachStatement, ControlFlowNodeType.LoopCondition);
Connect(data, conditionNode);
@@ -566,7 +573,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
continueTargets.Pop();
Connect(conditionNode, end);
-
+ builder.nodes.Add(end);
return end;
}
@@ -602,7 +609,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public override ControlFlowNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, ControlFlowNode data)
{
- ControlFlowNode end = builder.CreateEndNode(tryCatchStatement);
+ ControlFlowNode end = builder.CreateEndNode(tryCatchStatement, addToNodeList: false);
var edge = Connect(HandleEmbeddedStatement(tryCatchStatement.TryBlock, data), end);
if (!tryCatchStatement.FinallyBlock.IsNull)
edge.AddJumpOutOfTryFinally(tryCatchStatement);
@@ -616,6 +623,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
// Consumers of the CFG will have to special-case try-finally.
HandleEmbeddedStatement(tryCatchStatement.FinallyBlock, data);
}
+ builder.nodes.Add(end);
return end;
}
diff --git a/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
index c02b5ea30f..fb723aedce 100644
--- a/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
+++ b/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
@@ -44,21 +44,40 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
///
public class DefiniteAssignmentAnalysis
{
- readonly ControlFlowGraphBuilder cfgBuilder = new ControlFlowGraphBuilder();
+ sealed class DefiniteAssignmentNode : ControlFlowNode
+ {
+ public int Index;
+ public DefiniteAssignmentStatus NodeStatus;
+
+ public DefiniteAssignmentNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
+ : base(previousStatement, nextStatement, type)
+ {
+ }
+ }
+
+ sealed class DerivedControlFlowGraphBuilder : ControlFlowGraphBuilder
+ {
+ protected override ControlFlowNode CreateNode(Statement previousStatement, Statement nextStatement, ControlFlowNodeType type)
+ {
+ return new DefiniteAssignmentNode(previousStatement, nextStatement, type);
+ }
+ }
+
+ readonly DerivedControlFlowGraphBuilder cfgBuilder = new DerivedControlFlowGraphBuilder();
readonly DefiniteAssignmentVisitor visitor = new DefiniteAssignmentVisitor();
- readonly List allNodes = new List();
- readonly Dictionary beginNodeDict = new Dictionary();
- readonly Dictionary endNodeDict = new Dictionary();
- readonly Dictionary conditionNodeDict = new Dictionary();
+ readonly List allNodes = new List();
+ readonly Dictionary beginNodeDict = new Dictionary();
+ readonly Dictionary endNodeDict = new Dictionary();
+ readonly Dictionary conditionNodeDict = new Dictionary();
readonly ResolveVisitor resolveVisitor;
readonly CancellationToken cancellationToken;
- Dictionary nodeStatus = new Dictionary();
Dictionary edgeStatus = new Dictionary();
string variableName;
List unassignedVariableUses = new List();
+ int analyzedRangeStart, analyzedRangeEnd;
- Queue nodesWithModifiedInput = new Queue();
+ Queue nodesWithModifiedInput = new Queue();
public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken = default(CancellationToken))
: this(rootStatement, null, cancellationToken)
@@ -83,20 +102,18 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
cfgBuilder.EvaluateOnlyPrimitiveConstants = true;
}
- allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolveVisitor));
- 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(cfgBuilder.BuildControlFlowGraph(ame.Body, resolveVisitor));
- LambdaExpression lambda = descendant as LambdaExpression;
- if (lambda != null && lambda.Body is Statement)
- allNodes.AddRange(cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor));
- }
- // Verify that we created nodes for all statements:
- Debug.Assert(!rootStatement.DescendantsAndSelf.OfType().Except(allNodes.Select(n => n.NextStatement)).Any());
- // Now register the nodes in the dictionaries:
- foreach (ControlFlowNode node in allNodes) {
+ allNodes.AddRange(cfgBuilder.BuildControlFlowGraph(rootStatement, resolveVisitor).Cast());
+ for (int i = 0; i < allNodes.Count; i++) {
+ DefiniteAssignmentNode node = allNodes[i];
+ node.Index = i; // assign numbers to the nodes
+ if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements) {
+ // Anonymous methods have separate control flow graphs, but we also need to analyze those.
+ // Iterate backwards so that anonymous methods are inserted in the correct order
+ for (AstNode child = node.NextStatement.LastChild; child != null; child = child.PrevSibling) {
+ InsertAnonymousMethods(i + 1, child);
+ }
+ }
+ // Now register the node in the dictionaries:
if (node.Type == ControlFlowNodeType.StartNode || node.Type == ControlFlowNodeType.BetweenStatements)
beginNodeDict.Add(node.NextStatement, node);
if (node.Type == ControlFlowNodeType.BetweenStatements || node.Type == ControlFlowNodeType.EndNode)
@@ -104,6 +121,32 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
if (node.Type == ControlFlowNodeType.LoopCondition)
conditionNodeDict.Add(node.NextStatement, node);
}
+ // Verify that we created nodes for all statements:
+ Debug.Assert(!rootStatement.DescendantsAndSelf.OfType().Except(allNodes.Select(n => n.NextStatement)).Any());
+ this.analyzedRangeStart = 0;
+ this.analyzedRangeEnd = allNodes.Count - 1;
+ }
+
+ void InsertAnonymousMethods(int insertPos, AstNode node)
+ {
+ // Ignore any statements, as those have their own ControlFlowNode and get handled separately
+ if (node is Statement)
+ return;
+ AnonymousMethodExpression ame = node as AnonymousMethodExpression;
+ if (ame != null) {
+ allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph(ame.Body, resolveVisitor).Cast());
+ return;
+ }
+ LambdaExpression lambda = node as LambdaExpression;
+ if (lambda != null && lambda.Body is Statement) {
+ allNodes.InsertRange(insertPos, cfgBuilder.BuildControlFlowGraph((Statement)lambda.Body, resolveVisitor).Cast());
+ return;
+ }
+ // Descend into child expressions
+ // Iterate backwards so that anonymous methods are inserted in the correct order
+ for (AstNode child = node.LastChild; child != null; child = child.PrevSibling) {
+ InsertAnonymousMethods(insertPos, child);
+ }
}
///
@@ -115,21 +158,37 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
}
+ ///
+ /// Sets the range of statements to be analyzed.
+ /// This method can be used to restrict the analysis to only a part of the method.
+ /// Only the control flow paths that are fully contained within the selected part will be analyzed.
+ ///
+ /// Both 'start' and 'end' are inclusive.
+ public void SetAnalyzedRange(Statement start, Statement end)
+ {
+ int startIndex = beginNodeDict[start].Index;
+ int endIndex = endNodeDict[end].Index;
+ if (startIndex > endIndex)
+ throw new ArgumentException("The start statement must be lexically preceding the end statement");
+ this.analyzedRangeStart = startIndex;
+ this.analyzedRangeEnd = endIndex;
+ }
+
public void Analyze(string variable, DefiniteAssignmentStatus initialStatus = DefiniteAssignmentStatus.PotentiallyAssigned)
{
this.variableName = variable;
// Reset the status:
unassignedVariableUses.Clear();
- foreach (ControlFlowNode node in allNodes) {
- nodeStatus[node] = DefiniteAssignmentStatus.CodeUnreachable;
+ foreach (DefiniteAssignmentNode node in allNodes) {
+ node.NodeStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Outgoing)
edgeStatus[edge] = DefiniteAssignmentStatus.CodeUnreachable;
}
- ChangeNodeStatus(allNodes[0], initialStatus);
+ ChangeNodeStatus(allNodes[analyzedRangeStart], initialStatus);
// Iterate as long as the input status of some nodes is changing:
while (nodesWithModifiedInput.Count > 0) {
- ControlFlowNode node = nodesWithModifiedInput.Dequeue();
+ DefiniteAssignmentNode node = nodesWithModifiedInput.Dequeue();
DefiniteAssignmentStatus inputStatus = DefiniteAssignmentStatus.CodeUnreachable;
foreach (ControlFlowEdge edge in node.Incoming) {
inputStatus = MergeStatus(inputStatus, edgeStatus[edge]);
@@ -140,17 +199,17 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
public DefiniteAssignmentStatus GetStatusBefore(Statement statement)
{
- return nodeStatus[beginNodeDict[statement]];
+ return beginNodeDict[statement].NodeStatus;
}
public DefiniteAssignmentStatus GetStatusAfter(Statement statement)
{
- return nodeStatus[endNodeDict[statement]];
+ return endNodeDict[statement].NodeStatus;
}
public DefiniteAssignmentStatus GetStatusBeforeLoopCondition(Statement statement)
{
- return nodeStatus[conditionNodeDict[statement]];
+ return conditionNodeDict[statement].NodeStatus;
}
///
@@ -161,7 +220,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
GraphVizGraph g = new GraphVizGraph();
g.Title = "DefiniteAssignment - " + variableName;
for (int i = 0; i < allNodes.Count; i++) {
- string name = nodeStatus[allNodes[i]].ToString() + Environment.NewLine;
+ string name = "#" + i + " = " + allNodes[i].NodeStatus.ToString() + Environment.NewLine;
switch (allNodes[i].Type) {
case ControlFlowNodeType.StartNode:
case ControlFlowNodeType.BetweenStatements:
@@ -179,7 +238,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
}
g.AddNode(new GraphVizNode(i) { label = name });
foreach (ControlFlowEdge edge in allNodes[i].Outgoing) {
- GraphVizEdge ge = new GraphVizEdge(i, allNodes.IndexOf(edge.To));
+ GraphVizEdge ge = new GraphVizEdge(i, ((DefiniteAssignmentNode)edge.To).Index);
if (edgeStatus.Count > 0)
ge.label = edgeStatus[edge].ToString();
if (edge.IsLeavingTryFinally)
@@ -218,11 +277,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return DefiniteAssignmentStatus.PotentiallyAssigned;
}
- void ChangeNodeStatus(ControlFlowNode node, DefiniteAssignmentStatus inputStatus)
+ void ChangeNodeStatus(DefiniteAssignmentNode node, DefiniteAssignmentStatus inputStatus)
{
- if (nodeStatus[node] == inputStatus)
+ if (node.NodeStatus == inputStatus)
return;
- nodeStatus[node] = inputStatus;
+ node.NodeStatus = inputStatus;
DefiniteAssignmentStatus outputStatus;
switch (node.Type) {
case ControlFlowNodeType.StartNode:
@@ -312,7 +371,9 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
throw new InvalidOperationException("Invalid value for DefiniteAssignmentStatus");
}
edgeStatus[edge] = newStatus;
- nodesWithModifiedInput.Enqueue(edge.To);
+ DefiniteAssignmentNode targetNode = (DefiniteAssignmentNode)edge.To;
+ if (analyzedRangeStart <= targetNode.Index && targetNode.Index <= analyzedRangeEnd)
+ nodesWithModifiedInput.Enqueue(targetNode);
}
///
@@ -321,7 +382,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// The constant value of the expression; or null if the expression is not a constant.
ConstantResolveResult EvaluateConstant(Expression expr)
{
- return cfgBuilder.EvaluateConstant(expr);
+ if (resolveVisitor.TypeResolveContext is MinimalResolveContext) {
+ if (!(expr is PrimitiveExpression || expr is NullReferenceExpression))
+ return null;
+ }
+ return resolveVisitor.Resolve(expr) as ConstantResolveResult;
}
///
@@ -330,7 +395,11 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// The value of the constant boolean expression; or null if the value is not a constant boolean expression.
bool? EvaluateCondition(Expression expr)
{
- return cfgBuilder.EvaluateCondition(expr);
+ ConstantResolveResult rr = EvaluateConstant(expr);
+ if (rr != null)
+ return rr.ConstantValue as bool?;
+ else
+ return null;
}
static DefiniteAssignmentStatus CleanSpecialValues(DefiniteAssignmentStatus status)
@@ -633,10 +702,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
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);
- }
+ analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
return data;
}
@@ -644,10 +710,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
Statement body = lambdaExpression.Body as Statement;
if (body != null) {
- foreach (ControlFlowNode node in analysis.allNodes) {
- if (node.NextStatement == body)
- analysis.ChangeNodeStatus(node, data);
- }
+ analysis.ChangeNodeStatus(analysis.beginNodeDict[body], data);
} else {
lambdaExpression.Body.AcceptVisitor(this, data);
}