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);
                }
            }
            /// <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);
            }
            /// <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);
            }
            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;
                }
            }
示例#5
0
        private StyleASTNode ParseVariableReference()
        {
            AdvanceIfTokenType(StyleTokenType.Dollar);
            VariableReferenceNode refNode = new VariableReferenceNode(AssertTokenTypeAndAdvance(StyleTokenType.Identifier));

            while (tokenStream.HasMoreTokens && AdvanceIfTokenType(StyleTokenType.Dot))
            {
                refNode.AddChildNode(StyleASTNodeFactory.DotAccessNode(AssertTokenTypeAndAdvance(StyleTokenType.Identifier)).WithLocation(tokenStream.Previous));
            }

            return(refNode);
        }
            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;
            }
 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;
 }
            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);
            }
            /// <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;
            }
            /// <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;
            }
            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);
                }
            }
            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;
                }
            }
            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 == null)
                    {
                        continue;
                    }
                    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 (IsInsideTryOrCatchBlock(node.References [0]))
                    {
                        continue;
                    }
                    ProcessNode(node, true, nodeStates);
                }
            }
			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;
					}
					if (nodeStates [nextNode] == NodeState.Processing)
						reachable = null;
				}

				// 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;
				}
			}
			void ProcessNodes (VariableReferenceNode startNode)
			{
				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;
					}

					var assignmentIndexes = new List<int> ();
					for (int i = 0; i < node.References.Count; i++) {
						if (IsAssignment (node.References [i]))
							assignmentIndexes.Add (i);
					}

					for (int i = 0; i < assignmentIndexes.Count - 1; i++) {
						var index1 = assignmentIndexes [i];
						var index2 = assignmentIndexes [i + 1];
						if (index1 + 1 == index2)
							AddIssue (node.References [index1]);
					}
					if (assignmentIndexes.Count > 0 &&
						assignmentIndexes [assignmentIndexes.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)
					ProcessNode (node, true, nodeStates);
			}