// // VariableReferenceGraph.cs // // Author: // Mansheng Yang // // Copyright (c) 2012 Mansheng Yang // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System.Collections.Generic; using System.Linq; using ICSharpCode.NRefactory.CSharp.Analysis; using ICSharpCode.NRefactory.CSharp.Resolver; namespace ICSharpCode.NRefactory.CSharp.Refactoring { class VariableReferenceNode { public IList References { get; private set; } public IList NextNodes { get; private set; } public IList PreviousNodes { get; private set; } public VariableReferenceNode () { References = new List (); NextNodes = new List (); PreviousNodes = new List (); } public void AddNextNode (VariableReferenceNode node) { NextNodes.Add (node); node.PreviousNodes.Add (this); } } class VariableReferenceGraphBuilder { static ControlFlowGraphBuilder cfgBuilder = new ControlFlowGraphBuilder (); static CfgVariableReferenceNodeBuilder cfgVrNodeBuilder = new CfgVariableReferenceNodeBuilder (); public static VariableReferenceNode Build (ISet references, CSharpAstResolver resolver, Expression expression) { return ExpressionNodeCreationVisitor.CreateNode (references, resolver, new [] { expression }); } public static VariableReferenceNode Build (Statement statement, ISet references, ISet refStatements, BaseRefactoringContext context) { var cfg = cfgBuilder.BuildControlFlowGraph (statement, context.Resolver, context.CancellationToken); return cfgVrNodeBuilder.Build (cfg [0], references, refStatements, context.Resolver); } class GetExpressionsVisitor : DepthFirstAstVisitor> { public override IEnumerable VisitIfElseStatement (IfElseStatement ifElseStatement) { yield return ifElseStatement.Condition; } public override IEnumerable VisitSwitchStatement (SwitchStatement switchStatement) { yield return switchStatement.Expression; } public override IEnumerable VisitForStatement (ForStatement forStatement) { yield return forStatement.Condition; } public override IEnumerable VisitDoWhileStatement (DoWhileStatement doWhileStatement) { yield return doWhileStatement.Condition; } public override IEnumerable VisitWhileStatement (WhileStatement whileStatement) { yield return whileStatement.Condition; } public override IEnumerable VisitForeachStatement (ForeachStatement foreachStatement) { yield return foreachStatement.InExpression; } public override IEnumerable VisitExpressionStatement (ExpressionStatement expressionStatement) { yield return expressionStatement.Expression; } public override IEnumerable VisitLockStatement (LockStatement lockStatement) { yield return lockStatement.Expression; } public override IEnumerable VisitReturnStatement (ReturnStatement returnStatement) { yield return returnStatement.Expression; } public override IEnumerable VisitThrowStatement (ThrowStatement throwStatement) { yield return throwStatement.Expression; } public override IEnumerable VisitUsingStatement (UsingStatement usingStatement) { var expr = usingStatement.ResourceAcquisition as Expression; if (expr != null) return new [] { expr }; return usingStatement.ResourceAcquisition.AcceptVisitor (this); } public override IEnumerable VisitVariableDeclarationStatement ( VariableDeclarationStatement variableDeclarationStatement) { return variableDeclarationStatement.Variables.Select (v => v.Initializer); } public override IEnumerable VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement) { yield return yieldReturnStatement.Expression; } } class CfgVariableReferenceNodeBuilder { static GetExpressionsVisitor getExpr = new GetExpressionsVisitor (); ISet references; ISet refStatements; CSharpAstResolver resolver; Dictionary nodeDict; public VariableReferenceNode Build (ControlFlowNode startNode, ISet references, ISet refStatements, CSharpAstResolver resolver) { this.references = references; this.refStatements = refStatements; this.resolver = resolver; nodeDict = new Dictionary (); return AddNode (startNode); } static bool IsValidControlFlowNode (ControlFlowNode node) { if (node.NextStatement == null) return false; if (node.Type == ControlFlowNodeType.LoopCondition) { if (node.NextStatement is ForeachStatement) return false; } else { if (node.NextStatement is WhileStatement || node.NextStatement is DoWhileStatement || node.NextStatement is ForStatement) return false; } return true; } VariableReferenceNode GetStatementEndNode (VariableReferenceNode currentNode, Statement statement) { var expressions = statement.AcceptVisitor (getExpr); VariableReferenceNode endNode; ExpressionNodeCreationVisitor.CreateNode (references, resolver, expressions, currentNode, out endNode); return endNode; } VariableReferenceNode AddNode (ControlFlowNode startNode) { var node = new VariableReferenceNode (); var cfNode = startNode; while (true) { 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; } } return nodeDict [startNode]; } } class ExpressionNodeCreationVisitor : DepthFirstAstVisitor { VariableReferenceNode startNode; VariableReferenceNode endNode; ISet references; CSharpAstResolver resolver; ExpressionNodeCreationVisitor (ISet references, CSharpAstResolver resolver, VariableReferenceNode startNode) { this.references = references; this.resolver = resolver; this.startNode = this.endNode = startNode ?? new VariableReferenceNode (); } public static VariableReferenceNode CreateNode (ISet references, CSharpAstResolver resolver, params Expression [] expressions) { VariableReferenceNode endNode; return CreateNode (references, resolver, expressions, null, out endNode); } public static VariableReferenceNode CreateNode (ISet references, CSharpAstResolver resolver, IEnumerable 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; } static ExpressionNodeCreationVisitor CreateVisitor (ISet 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; } static VariableReferenceNode CreateNode (ISet references, CSharpAstResolver resolver, Expression rootExpr, VariableReferenceNode startNode = null, VariableReferenceNode nextNode = null) { return CreateVisitor (references, resolver, rootExpr, startNode, nextNode).startNode; } #region Skipped Expressions public override void VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression) { } public override void VisitLambdaExpression (LambdaExpression lambdaExpression) { } public override void VisitBaseReferenceExpression (BaseReferenceExpression baseReferenceExpression) { } public override void VisitNullReferenceExpression (NullReferenceExpression nullReferenceExpression) { } public override void VisitPrimitiveExpression (PrimitiveExpression primitiveExpression) { } public override void VisitSizeOfExpression (SizeOfExpression sizeOfExpression) { } public override void VisitThisReferenceExpression (ThisReferenceExpression thisReferenceExpression) { } public override void VisitTypeOfExpression (TypeOfExpression typeOfExpression) { } public override void VisitTypeReferenceExpression (TypeReferenceExpression typeReferenceExpression) { } public override void VisitUndocumentedExpression (UndocumentedExpression undocumentedExpression) { } public override void VisitDefaultValueExpression (DefaultValueExpression defaultValueExpression) { } public override void VisitEmptyExpression (EmptyExpression emptyExpression) { } #endregion public override void VisitAssignmentExpression (AssignmentExpression assignmentExpression) { assignmentExpression.Right.AcceptVisitor (this); assignmentExpression.Left.AcceptVisitor (this); } public override void VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression) { binaryOperatorExpression.Left.AcceptVisitor (this); binaryOperatorExpression.Right.AcceptVisitor (this); } public override void VisitCastExpression (CastExpression castExpression) { castExpression.Expression.AcceptVisitor (this); } public override void VisitCheckedExpression (CheckedExpression checkedExpression) { checkedExpression.Expression.AcceptVisitor (this); } 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; } public override void VisitIdentifierExpression (IdentifierExpression identifierExpression) { if (references.Contains (identifierExpression)) endNode.References.Add (identifierExpression); } public override void VisitIndexerExpression (IndexerExpression indexerExpression) { indexerExpression.Target.AcceptVisitor (this); foreach (var arg in indexerExpression.Arguments) arg.AcceptVisitor (this); } public override void VisitInvocationExpression (InvocationExpression invocationExpression) { invocationExpression.Target.AcceptVisitor (this); var outArguments = new List (); foreach (var arg in invocationExpression.Arguments) { var directionExpr = arg as DirectionExpression; if (directionExpr != null && directionExpr.FieldDirection == FieldDirection.Out) { outArguments.Add (directionExpr); continue; } arg.AcceptVisitor (this); } foreach (var arg in outArguments) arg.AcceptVisitor (this); } public override void VisitDirectionExpression (DirectionExpression directionExpression) { directionExpression.Expression.AcceptVisitor (this); } public override void VisitMemberReferenceExpression (MemberReferenceExpression memberReferenceExpression) { memberReferenceExpression.Target.AcceptVisitor (this); } public override void VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression) { foreach (var arg in objectCreateExpression.Arguments) arg.AcceptVisitor (this); objectCreateExpression.Initializer.AcceptVisitor (this); } public override void VisitAnonymousTypeCreateExpression ( AnonymousTypeCreateExpression anonymousTypeCreateExpression) { foreach (var init in anonymousTypeCreateExpression.Initializers) init.AcceptVisitor (this); } public override void VisitArrayCreateExpression (ArrayCreateExpression arrayCreateExpression) { foreach (var arg in arrayCreateExpression.Arguments) arg.AcceptVisitor (this); arrayCreateExpression.Initializer.AcceptVisitor (this); } public override void VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression) { parenthesizedExpression.Expression.AcceptVisitor (this); } public override void VisitPointerReferenceExpression (PointerReferenceExpression pointerReferenceExpression) { pointerReferenceExpression.Target.AcceptVisitor (this); } public override void VisitStackAllocExpression (StackAllocExpression stackAllocExpression) { stackAllocExpression.CountExpression.AcceptVisitor (this); } public override void VisitUnaryOperatorExpression (UnaryOperatorExpression unaryOperatorExpression) { unaryOperatorExpression.Expression.AcceptVisitor (this); } public override void VisitUncheckedExpression (UncheckedExpression uncheckedExpression) { uncheckedExpression.Expression.AcceptVisitor (this); } public override void VisitAsExpression (AsExpression asExpression) { asExpression.Expression.AcceptVisitor (this); } public override void VisitIsExpression (IsExpression isExpression) { isExpression.Expression.AcceptVisitor (this); } public override void VisitArrayInitializerExpression (ArrayInitializerExpression arrayInitializerExpression) { foreach (var element in arrayInitializerExpression.Elements) element.AcceptVisitor (this); } public override void VisitNamedArgumentExpression (NamedArgumentExpression namedArgumentExpression) { namedArgumentExpression.Expression.AcceptVisitor (this); } public override void VisitNamedExpression (NamedExpression namedExpression) { namedExpression.Expression.AcceptVisitor (this); } public override void VisitQueryExpression (QueryExpression queryExpression) { foreach (var clause in queryExpression.Clauses) clause.AcceptVisitor (this); } #region Query Clauses public override void VisitQueryContinuationClause (QueryContinuationClause queryContinuationClause) { queryContinuationClause.PrecedingQuery.AcceptVisitor (this); } public override void VisitQueryFromClause (QueryFromClause queryFromClause) { queryFromClause.Expression.AcceptVisitor (this); } public override void VisitQueryJoinClause (QueryJoinClause queryJoinClause) { queryJoinClause.InExpression.AcceptVisitor (this); } public override void VisitQueryLetClause (QueryLetClause queryLetClause) { } public override void VisitQueryWhereClause (QueryWhereClause queryWhereClause) { } public override void VisitQueryOrderClause (QueryOrderClause queryOrderClause) { } public override void VisitQueryOrdering (QueryOrdering queryOrdering) { } public override void VisitQuerySelectClause (QuerySelectClause querySelectClause) { } public override void VisitQueryGroupClause (QueryGroupClause queryGroupClause) { } #endregion } } }