void FindMultipleEnumeration(VariableReferenceNode startNode)
            {
                var vrg = GetAssignmentUsageGraph(startNode);

                visitedNodes   = new HashSet <VariableReferenceNode> ();
                collectedNodes = new HashSet <VariableReferenceNode> ();

                // degree of a node is the number of references that can be reached by the node
                nodeDegree = new Dictionary <VariableReferenceNode, int> ();

                foreach (var node in vrg)
                {
                    if (node.References.Count == 0 || !visitedNodes.Add(node))
                    {
                        continue;
                    }
                    ProcessNode(node);
                    if (nodeDegree [node] > 1)
                    {
                        collectedNodes.Add(node);
                    }
                }
                foreach (var node in collectedNodes)
                {
                    AddIssues(node.References);
                }
            }
 ExpressionNodeCreationVisitor(ISet <AstNode> references, CSharpAstResolver resolver,
                               VariableReferenceNode startNode)
 {
     this.references = references;
     this.resolver   = resolver;
     this.startNode  = this.endNode = startNode ?? new VariableReferenceNode();
 }
            /// <summary>
            /// convert a variable reference graph starting from the specified node to an assignment usage graph,
            /// in which nodes are connect if and only if they contains references using the same assigned value
            /// </summary>
            /// <param name="startNode">starting node of the variable reference graph</param>
            /// <returns>
            /// list of VariableReferenceNode, each of which is a starting node of a sub-graph in which references all
            /// use the same assigned value
            /// </returns>
            static IEnumerable <VariableReferenceNode> GetAssignmentUsageGraph(VariableReferenceNode startNode)
            {
                var graph   = new List <VariableReferenceNode> ();
                var visited = new HashSet <VariableReferenceNode> ();
                var stack   = new Stack <VariableReferenceNode> ();

                stack.Push(startNode);
                while (stack.Count > 0)
                {
                    var node = stack.Pop();
                    if (!visited.Add(node))
                    {
                        continue;
                    }

                    var nodes = SplitNode(node);
                    graph.AddRange(nodes);
                    foreach (var addedNode in nodes)
                    {
                        visited.Add(addedNode);
                    }

                    foreach (var nextNode in nodes.Last().NextNodes)
                    {
                        stack.Push(nextNode);
                    }
                }
                return(graph);
            }
            public override void VisitConditionalExpression(ConditionalExpression conditionalExpression)
            {
                conditionalExpression.Condition.AcceptVisitor(this);
                var resolveResult = resolver.Resolve(conditionalExpression.Condition);

                if (resolveResult.ConstantValue is bool)
                {
                    if ((bool)resolveResult.ConstantValue)
                    {
                        conditionalExpression.TrueExpression.AcceptVisitor(this);
                    }
                    else
                    {
                        conditionalExpression.FalseExpression.AcceptVisitor(this);
                    }
                    return;
                }
                var nextEndNode = new VariableReferenceNode();
                var trueNode    = CreateNode(references, resolver, conditionalExpression.TrueExpression, null,
                                             nextEndNode);
                var falseNode = CreateNode(references, resolver, conditionalExpression.FalseExpression, null,
                                           nextEndNode);

                endNode.AddNextNode(trueNode);
                endNode.AddNextNode(falseNode);
                endNode = nextEndNode;
            }
            VariableReferenceNode GetStatementEndNode(VariableReferenceNode currentNode, Statement statement)
            {
                var expressions = statement.AcceptVisitor(getExpr);
                VariableReferenceNode endNode;

                ExpressionNodeCreationVisitor.CreateNode(references, resolver, expressions, currentNode, out endNode);
                return(endNode);
            }
            /// <summary>
            /// split references in the specified node into sub nodes according to the value they uses
            /// </summary>
            /// <param name="node">node to split</param>
            /// <returns>list of sub nodes</returns>
            static IList <VariableReferenceNode> SplitNode(VariableReferenceNode node)
            {
                var subNodes = new List <VariableReferenceNode> ();
                // find indices of all assignments in node and use them to split references
                var assignmentIndices = new List <int> {
                    -1
                };

                for (int i = 0; i < node.References.Count; i++)
                {
                    if (IsAssignment(node.References [i]))
                    {
                        assignmentIndices.Add(i);
                    }
                }
                assignmentIndices.Add(node.References.Count);
                for (int i = 0; i < assignmentIndices.Count - 1; i++)
                {
                    var index1 = assignmentIndices [i];
                    var index2 = assignmentIndices [i + 1];
                    if (index1 + 1 >= index2)
                    {
                        continue;
                    }
                    var subNode = new VariableReferenceNode();
                    for (int refIndex = index1 + 1; refIndex < index2; refIndex++)
                    {
                        subNode.References.Add(node.References [refIndex]);
                    }
                    subNodes.Add(subNode);
                }
                if (subNodes.Count == 0)
                {
                    subNodes.Add(new VariableReferenceNode());
                }

                var firstNode = subNodes [0];

                foreach (var prevNode in node.PreviousNodes)
                {
                    prevNode.NextNodes.Remove(node);
                    // connect two nodes if the first ref is not an assignment
                    if (firstNode.References.FirstOrDefault() == node.References.FirstOrDefault())
                    {
                        prevNode.NextNodes.Add(firstNode);
                    }
                }

                var lastNode = subNodes [subNodes.Count - 1];

                foreach (var nextNode in node.NextNodes)
                {
                    nextNode.PreviousNodes.Remove(node);
                    lastNode.AddNextNode(nextNode);
                }

                return(subNodes);
            }
Example #7
0
            void ProcessNode(VariableReferenceNode node, bool addIssue,
                             IDictionary <VariableReferenceNode, NodeState> nodeStates)
            {
                if (nodeStates [node] == NodeState.None)
                {
                    nodeStates [node] = NodeState.Processing;
                }

                bool?reachable = false;

                foreach (var nextNode in node.NextNodes)
                {
                    if (nodeStates [nextNode] == NodeState.None)
                    {
                        ProcessNode(nextNode, false, nodeStates);
                    }

                    if (nodeStates [nextNode] == NodeState.UsageReachable)
                    {
                        reachable = true;
                        break;
                    }
                    // downstream nodes are not fully processed (e.g. due to loop), there is no enough info to
                    // determine the node state
                    if (nodeStates [nextNode] != NodeState.UsageUnreachable)
                    {
                        reachable = null;
                    }
                }

                // add issue if it is not possible to reach any usage via NextNodes
                if (addIssue && reachable == false)
                {
                    AddIssue(node.References [node.References.Count - 1]);
                }

                if (nodeStates [node] != NodeState.Processing)
                {
                    return;
                }

                switch (reachable)
                {
                case null:
                    nodeStates [node] = NodeState.None;
                    break;

                case true:
                    nodeStates [node] = NodeState.UsageReachable;
                    break;

                case false:
                    nodeStates [node] = NodeState.UsageUnreachable;
                    break;
                }
            }
            static ExpressionNodeCreationVisitor CreateVisitor(ISet <AstNode> references, CSharpAstResolver resolver,
                                                               Expression rootExpr, VariableReferenceNode startNode = null, VariableReferenceNode nextNode = null)
            {
                var visitor = new ExpressionNodeCreationVisitor(references, resolver, startNode);

                rootExpr.AcceptVisitor(visitor);
                if (nextNode != null)
                {
                    visitor.endNode.AddNextNode(nextNode);
                }
                return(visitor);
            }
 public static VariableReferenceNode CreateNode(ISet <AstNode> references, CSharpAstResolver resolver,
                                                IEnumerable <Expression> expressions, VariableReferenceNode startNode, out VariableReferenceNode endNode)
 {
     startNode = startNode ?? new VariableReferenceNode();
     endNode   = startNode;
     foreach (var expr in expressions)
     {
         var visitor = CreateVisitor(references, resolver, expr, endNode);
         endNode = visitor.endNode;
     }
     return(startNode);
 }
            VariableReferenceNode AddNode(ControlFlowNode startNode)
            {
                var node   = new VariableReferenceNode();
                var cfNode = startNode;

                while (true)
                {
                    if (variableReferenceGraphBuilder.ctx.CancellationToken.IsCancellationRequested)
                    {
                        return(null);
                    }
                    if (nodeDict.ContainsKey(cfNode))
                    {
                        node.AddNextNode(nodeDict [cfNode]);
                        break;
                    }
                    // create a new node for fork point
                    if (cfNode.Incoming.Count > 1 || cfNode.Outgoing.Count > 1)
                    {
                        nodeDict [cfNode] = node;

                        var forkNode = new VariableReferenceNode();
                        node.AddNextNode(forkNode);
                        node = forkNode;
                    }
                    nodeDict [cfNode] = node;

                    if (IsValidControlFlowNode(cfNode) && refStatements.Contains(cfNode.NextStatement))
                    {
                        node = GetStatementEndNode(node, cfNode.NextStatement);
                    }

                    if (cfNode.Outgoing.Count == 1)
                    {
                        cfNode = cfNode.Outgoing [0].To;
                    }
                    else
                    {
                        foreach (var e in cfNode.Outgoing)
                        {
                            node.AddNextNode(AddNode(e.To));
                        }
                        break;
                    }
                }
                VariableReferenceNode result;

                if (!nodeDict.TryGetValue(startNode, out result))
                {
                    return(new VariableReferenceNode());
                }
                return(result);
            }
            void ProcessNode(VariableReferenceNode node)
            {
                var degree = nodeDegree [node] = 0;

                foreach (var nextNode in node.NextNodes)
                {
                    collectedNodes.Add(nextNode);
                    if (visitedNodes.Add(nextNode))
                    {
                        ProcessNode(nextNode);
                    }
                    degree = Math.Max(degree, nodeDegree [nextNode]);
                }
                nodeDegree [node] = degree + node.References.Count;
            }
Example #12
0
            void ProcessNodes(VariableReferenceNode startNode)
            {
                // node state of a node indicates whether it is possible for an upstream node to reach any usage via
                // the node
                var nodeStates  = new Dictionary <VariableReferenceNode, NodeState>();
                var assignments = new List <VariableReferenceNode>();

                // dfs to preprocess all nodes and find nodes which end with assignment
                var stack = new Stack <VariableReferenceNode>();

                stack.Push(startNode);
                while (stack.Count > 0)
                {
                    var node = stack.Pop();
                    if (node.References.Count > 0)
                    {
                        nodeStates [node] = IsAssignment(node.References [0]) ?
                                            NodeState.UsageUnreachable : NodeState.UsageReachable;
                    }
                    else
                    {
                        nodeStates [node] = NodeState.None;
                    }

                    // find indices of all assignments in node.References
                    var assignmentIndices = new List <int>();
                    for (int i = 0; i < node.References.Count; i++)
                    {
                        if (IsAssignment(node.References [i]))
                        {
                            assignmentIndices.Add(i);
                        }
                    }
                    // for two consecutive assignments, the first one is redundant
                    for (int i = 0; i < assignmentIndices.Count - 1; i++)
                    {
                        var index1 = assignmentIndices [i];
                        var index2 = assignmentIndices [i + 1];
                        if (index1 + 1 == index2)
                        {
                            AddIssue(node.References [index1]);
                        }
                    }
                    // if the node ends with an assignment, add it to assignments so as to check if it is redundant
                    // later
                    if (assignmentIndices.Count > 0 &&
                        assignmentIndices [assignmentIndices.Count - 1] == node.References.Count - 1)
                    {
                        assignments.Add(node);
                    }

                    foreach (var nextNode in node.NextNodes)
                    {
                        if (!nodeStates.ContainsKey(nextNode))
                        {
                            stack.Push(nextNode);
                        }
                    }
                }

                foreach (var node in assignments)
                {
                    // we do not analyze an assignment inside a try block as it can jump to any catch block or finally block
                    if (IsInsideTryBlock(node.References [0]))
                    {
                        continue;
                    }
                    ProcessNode(node, true, nodeStates);
                }
            }
 public void AddNextNode(VariableReferenceNode node)
 {
     NextNodes.Add(node);
     node.PreviousNodes.Add(this);
 }
 static VariableReferenceNode CreateNode(ISet <AstNode> references, CSharpAstResolver resolver,
                                         Expression rootExpr, VariableReferenceNode startNode = null, VariableReferenceNode nextNode = null)
 {
     return(CreateVisitor(references, resolver, rootExpr, startNode, nextNode).startNode);
 }