Browse Source

[CodeIssue] Added MultipleEnumerationIssue

newNRvisualizers
Mansheng Yang 13 years ago
parent
commit
e6abbcb949
  1. 2
      ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  2. 354
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/MultipleEnumerationIssue.cs
  3. 540
      ICSharpCode.NRefactory.CSharp/Refactoring/VariableReferenceGraph.cs
  4. 679
      ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/MultipleEnumerationIssueTests.cs
  5. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

2
ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj

@ -265,6 +265,7 @@ @@ -265,6 +265,7 @@
<Compile Include="Refactoring\CodeIssues\CompareFloatWithEqualityOperatorIssue.cs" />
<Compile Include="Refactoring\CodeIssues\ExplicitConversionInForEachIssue.cs" />
<Compile Include="Refactoring\CodeIssues\ForControlVariableNotModifiedIssue.cs" />
<Compile Include="Refactoring\CodeIssues\MultipleEnumerationIssue.cs" />
<Compile Include="Refactoring\CodeIssues\RedundantElseIssue.cs" />
<Compile Include="Refactoring\CodeIssues\RedundantFieldInitializerIssue.cs" />
<Compile Include="Refactoring\CodeIssues\UnreachableCodeIssue.cs" />
@ -286,6 +287,7 @@ @@ -286,6 +287,7 @@
<Compile Include="Refactoring\RefactoringContext.cs" />
<Compile Include="Refactoring\Script.cs" />
<Compile Include="Refactoring\TypeSystemAstBuilder.cs" />
<Compile Include="Refactoring\VariableReferenceGraph.cs" />
<Compile Include="Resolver\CompositeResolveVisitorNavigator.cs" />
<Compile Include="Resolver\DynamicInvocationResolveResult.cs" />
<Compile Include="Resolver\DynamicMemberResolveResult.cs" />

354
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/MultipleEnumerationIssue.cs

@ -0,0 +1,354 @@ @@ -0,0 +1,354 @@
//
// MultipleEnumerationIssue.cs
//
// Author:
// Mansheng Yang <lightyang0@gmail.com>
//
// Copyright (c) 2012 Mansheng Yang <lightyang0@gmail.com>
//
// 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;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
[IssueDescription ("Possible mutiple enumeration of IEnuemrable",
Description = "Possible multiple enumeration of IEnumerable.",
Category = IssueCategories.CodeQualityIssues,
Severity = Severity.Warning,
IssueMarker = IssueMarker.Underline)]
public class MultipleEnumerationIssue : ICodeIssueProvider
{
public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context)
{
var unit = context.RootNode as CompilationUnit;
if (unit == null)
return Enumerable.Empty<CodeIssue> ();
return new GatherVisitor (context, unit).GetIssues ();
}
class AnalysisStatementCollector : DepthFirstAstVisitor
{
List<Statement> statements;
AstNode variableDecl;
AnalysisStatementCollector (AstNode variableDecl)
{
this.variableDecl = variableDecl;
}
IList<Statement> GetStatements ()
{
if (statements != null)
return statements;
statements = new List<Statement> ();
var parent = variableDecl.Parent;
while (parent != null) {
if (parent is BlockStatement || parent is MethodDeclaration ||
parent is AnonymousMethodExpression || parent is LambdaExpression) {
parent.AcceptVisitor (this);
if (parent is BlockStatement)
statements.Add ((BlockStatement)parent);
break;
}
parent = parent.Parent;
}
return statements;
}
public override void VisitMethodDeclaration (MethodDeclaration methodDeclaration)
{
statements.Add (methodDeclaration.Body);
base.VisitMethodDeclaration (methodDeclaration);
}
public override void VisitAnonymousMethodExpression (AnonymousMethodExpression anonymousMethodExpression)
{
statements.Add (anonymousMethodExpression.Body);
base.VisitAnonymousMethodExpression (anonymousMethodExpression);
}
public override void VisitLambdaExpression (LambdaExpression lambdaExpression)
{
var body = lambdaExpression.Body as BlockStatement;
if (body != null)
statements.Add (body);
base.VisitLambdaExpression (lambdaExpression);
}
public static IList<Statement> Collect (AstNode variableDecl)
{
return new AnalysisStatementCollector (variableDecl).GetStatements ();
}
}
class GatherVisitor : GatherVisitorBase
{
static FindReferences refFinder = new FindReferences ();
CompilationUnit unit;
HashSet<AstNode> collectedAstNodes;
public GatherVisitor (BaseRefactoringContext ctx, CompilationUnit unit)
: base (ctx)
{
this.unit = unit;
this.collectedAstNodes = new HashSet<AstNode> ();
}
void AddIssue (AstNode node)
{
if (collectedAstNodes.Add (node))
AddIssue (node, ctx.TranslateString ("Possible multiple enumeration of IEnumerable"));
}
void AddIssues (IEnumerable<AstNode> nodes)
{
foreach (var node in nodes)
AddIssue (node);
}
public override void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration)
{
base.VisitParameterDeclaration (parameterDeclaration);
var resolveResult = ctx.Resolve (parameterDeclaration) as LocalResolveResult;
CollectIssues (parameterDeclaration, resolveResult);
}
public override void VisitVariableInitializer (VariableInitializer variableInitializer)
{
base.VisitVariableInitializer (variableInitializer);
var resolveResult = ctx.Resolve (variableInitializer) as LocalResolveResult;
CollectIssues (variableInitializer, resolveResult);
}
static bool IsAssignment (AstNode node)
{
var assignment = node.Parent as AssignmentExpression;
if (assignment != null)
return assignment.Left == node;
var direction = node.Parent as DirectionExpression;
if (direction != null)
return direction.FieldDirection == FieldDirection.Out && direction.Expression == node;
return false;
}
bool IsEnumeration (AstNode node)
{
var foreachStatement = node.Parent as ForeachStatement;
if (foreachStatement != null && foreachStatement.InExpression == node) {
return true;
}
var memberRef = node.Parent as MemberReferenceExpression;
if (memberRef != null && memberRef.Target == node) {
var invocation = memberRef.Parent as InvocationExpression;
if (invocation == null || invocation.Target != memberRef)
return false;
var methodGroup = ctx.Resolve (memberRef) as MethodGroupResolveResult;
if (methodGroup == null)
return false;
var method = methodGroup.Methods.FirstOrDefault ();
if (method != null) {
var declaringTypeDef = method.DeclaringTypeDefinition;
if (declaringTypeDef != null && declaringTypeDef.KnownTypeCode == KnownTypeCode.Object)
return false;
}
return true;
}
return false;
}
HashSet<AstNode> references;
HashSet<Statement> refStatements;
HashSet<LambdaExpression> lambdaExpressions;
HashSet<VariableReferenceNode> visitedNodes;
HashSet<VariableReferenceNode> collectedNodes;
Dictionary<VariableReferenceNode, int> nodeDegree; // number of enumerations a node can reach
void FindReferences (AstNode variableDecl, IVariable variable)
{
references = new HashSet<AstNode> ();
refStatements = new HashSet<Statement> ();
lambdaExpressions = new HashSet<LambdaExpression> ();
refFinder.FindLocalReferences (variable, ctx.ParsedFile, unit, ctx.Compilation,
(astNode, resolveResult) => {
if (astNode == variableDecl)
return;
var parent = astNode.Parent;
while (!(parent == null || parent is Statement || parent is LambdaExpression))
parent = parent.Parent;
if (parent == null)
return;
// lambda expression with expression body
var expr = parent as LambdaExpression;
if (expr != null) {
if (IsAssignment (astNode) || IsEnumeration (astNode)) {
references.Add (astNode);
lambdaExpressions.Add (expr);
}
return;
}
var statement = (Statement)parent;
if (IsAssignment (astNode) || IsEnumeration (astNode)) {
references.Add (astNode);
refStatements.Add (statement);
}
}, ctx.CancellationToken);
}
void CollectIssues (AstNode variableDecl, LocalResolveResult resolveResult)
{
if (resolveResult == null)
return;
var type = resolveResult.Type;
var typeDef = type.GetDefinition ();
if (typeDef == null ||
(typeDef.KnownTypeCode != KnownTypeCode.IEnumerable &&
typeDef.KnownTypeCode != KnownTypeCode.IEnumerableOfT))
return;
FindReferences (variableDecl, resolveResult.Variable);
var statements = AnalysisStatementCollector.Collect (variableDecl);
foreach (var statement in statements) {
var vrNode = VariableReferenceGraphBuilder.Build (statement, references, refStatements, ctx);
FindMultipleEnumeration (vrNode);
}
foreach (var lambda in lambdaExpressions) {
var vrNode = VariableReferenceGraphBuilder.Build (references, ctx.Resolver, (Expression)lambda.Body);
FindMultipleEnumeration (vrNode);
}
}
static IList<VariableReferenceNode> SplitNode (VariableReferenceNode node)
{
var subNodes = new List<VariableReferenceNode> ();
var assignmentIndexes = new List<int> { -1 };
for (int i = 0; i < node.References.Count; i++) {
if (IsAssignment (node.References [i]))
assignmentIndexes.Add (i);
}
assignmentIndexes.Add (node.References.Count);
for (int i = 0; i < assignmentIndexes.Count - 1; i++) {
var index1 = assignmentIndexes [i];
var index2 = assignmentIndexes [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;
}
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;
// split the node according to the assigned value
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 FindMultipleEnumeration (VariableReferenceNode startNode)
{
var vrg = GetAssignmentUsageGraph (startNode);
visitedNodes = new HashSet<VariableReferenceNode> ();
collectedNodes = new HashSet<VariableReferenceNode> ();
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);
}
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;
}
}
}
}

540
ICSharpCode.NRefactory.CSharp/Refactoring/VariableReferenceGraph.cs

@ -0,0 +1,540 @@ @@ -0,0 +1,540 @@
//
// VariableReferenceGraph.cs
//
// Author:
// Mansheng Yang <lightyang0@gmail.com>
//
// Copyright (c) 2012 Mansheng Yang <lightyang0@gmail.com>
//
// 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<AstNode> References {
get;
private set;
}
public IList<VariableReferenceNode> NextNodes {
get;
private set;
}
public IList<VariableReferenceNode> PreviousNodes {
get;
private set;
}
public VariableReferenceNode ()
{
References = new List<AstNode> ();
NextNodes = new List<VariableReferenceNode> ();
PreviousNodes = new List<VariableReferenceNode> ();
}
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<AstNode> references, CSharpAstResolver resolver,
Expression expression)
{
return ExpressionNodeCreationVisitor.CreateNode (references, resolver, new [] { expression });
}
public static VariableReferenceNode Build (Statement statement, ISet<AstNode> references,
ISet<Statement> refStatements, BaseRefactoringContext context)
{
var cfg = cfgBuilder.BuildControlFlowGraph (statement, context.Resolver, context.CancellationToken);
return cfgVrNodeBuilder.Build (cfg [0], references, refStatements, context.Resolver);
}
class GetExpressionsVisitor : DepthFirstAstVisitor<IEnumerable<Expression>>
{
public override IEnumerable<Expression> VisitIfElseStatement (IfElseStatement ifElseStatement)
{
yield return ifElseStatement.Condition;
}
public override IEnumerable<Expression> VisitSwitchStatement (SwitchStatement switchStatement)
{
yield return switchStatement.Expression;
}
public override IEnumerable<Expression> VisitForStatement (ForStatement forStatement)
{
yield return forStatement.Condition;
}
public override IEnumerable<Expression> VisitDoWhileStatement (DoWhileStatement doWhileStatement)
{
yield return doWhileStatement.Condition;
}
public override IEnumerable<Expression> VisitWhileStatement (WhileStatement whileStatement)
{
yield return whileStatement.Condition;
}
public override IEnumerable<Expression> VisitForeachStatement (ForeachStatement foreachStatement)
{
yield return foreachStatement.InExpression;
}
public override IEnumerable<Expression> VisitExpressionStatement (ExpressionStatement expressionStatement)
{
yield return expressionStatement.Expression;
}
public override IEnumerable<Expression> VisitLockStatement (LockStatement lockStatement)
{
yield return lockStatement.Expression;
}
public override IEnumerable<Expression> VisitReturnStatement (ReturnStatement returnStatement)
{
yield return returnStatement.Expression;
}
public override IEnumerable<Expression> VisitThrowStatement (ThrowStatement throwStatement)
{
yield return throwStatement.Expression;
}
public override IEnumerable<Expression> VisitUsingStatement (UsingStatement usingStatement)
{
var expr = usingStatement.ResourceAcquisition as Expression;
if (expr != null)
return new [] { expr };
return usingStatement.ResourceAcquisition.AcceptVisitor (this);
}
public override IEnumerable<Expression> VisitVariableDeclarationStatement (
VariableDeclarationStatement variableDeclarationStatement)
{
return variableDeclarationStatement.Variables.Select (v => v.Initializer);
}
public override IEnumerable<Expression> VisitYieldReturnStatement (YieldReturnStatement yieldReturnStatement)
{
yield return yieldReturnStatement.Expression;
}
}
class CfgVariableReferenceNodeBuilder
{
static GetExpressionsVisitor getExpr = new GetExpressionsVisitor ();
ISet<AstNode> references;
ISet<Statement> refStatements;
CSharpAstResolver resolver;
Dictionary<ControlFlowNode, VariableReferenceNode> nodeDict;
public VariableReferenceNode Build (ControlFlowNode startNode, ISet<AstNode> references,
ISet<Statement> refStatements, CSharpAstResolver resolver)
{
this.references = references;
this.refStatements = refStatements;
this.resolver = resolver;
nodeDict = new Dictionary<ControlFlowNode, VariableReferenceNode> ();
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<AstNode> references;
CSharpAstResolver resolver;
ExpressionNodeCreationVisitor (ISet<AstNode> references, CSharpAstResolver resolver,
VariableReferenceNode startNode)
{
this.references = references;
this.resolver = resolver;
this.startNode = this.endNode = startNode ?? new VariableReferenceNode ();
}
public static VariableReferenceNode CreateNode (ISet<AstNode> references, CSharpAstResolver resolver,
params Expression [] expressions)
{
VariableReferenceNode endNode;
return CreateNode (references, resolver, expressions, null, out endNode);
}
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;
}
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;
}
static VariableReferenceNode CreateNode (ISet<AstNode> 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<Expression> ();
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
}
}
}

679
ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/MultipleEnumerationIssueTests.cs

@ -0,0 +1,679 @@ @@ -0,0 +1,679 @@
//
// MultipleEnumerationIssueTests.cs
//
// Author:
// Mansheng Yang <lightyang0@gmail.com>
//
// Copyright (c) 2012 Mansheng Yang <lightyang0@gmail.com>
//
// 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 ICSharpCode.NRefactory.CSharp.Refactoring;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.CSharp.CodeIssues
{
[TestFixture]
public class MultipleEnumerationIssueTests : InspectionActionTestBase
{
[Test]
public void TestVariableInvocation ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod ()
{
IEnumerable<object> e = null;
var type = e.GetType();
var x = e.First ();
var y = e.Count ();
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestVariableForeach ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod ()
{
IEnumerable<object> e = null;
foreach (var x in e) ;
foreach (var y in e) ;
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestVariableMixed ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod ()
{
IEnumerable<object> e = null;
foreach (var x in e) ;
var y = e.Count ();
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestParameter ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (IEnumerable<object> e)
{
foreach (var x in e) ;
var y = e.Count ();
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestObjectMethodInvocation ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod ()
{
IEnumerable<object> e;
var a = e.GetType ();
var b = e.ToString ();
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestIf ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (int i)
{
IEnumerable<object> e;
if (i > 0) {
var a = e.Count ();
} else {
var b = e.First ();
var c = e.Count ();
}
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestIf2 ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (int i)
{
IEnumerable<object> e;
if (i > 0) {
var a = e.Count ();
} else {
var b = e.First ();
}
var c = e.Count ();
}
}";
Test<MultipleEnumerationIssue> (input, 3);
}
[Test]
public void TestIf3 ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (int i)
{
IEnumerable<object> e;
if (i > 0) {
var a = e.Count ();
} else {
var b = e.First ();
}
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestFor ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod ()
{
IEnumerable<object> e;
for (int i = 0; i < 10; i++) {
var a = e.Count ();
}
}
}";
Test<MultipleEnumerationIssue> (input, 1);
}
[Test]
public void TestWhile ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod ()
{
IEnumerable<object> e;
int i;
while (i > 1) {
var a = e.Count ();
}
}
}";
Test<MultipleEnumerationIssue> (input, 1);
}
[Test]
public void TestWhile2 ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod ()
{
IEnumerable<object> e;
int i;
while (i > e.Count ()) {
}
}
}";
Test<MultipleEnumerationIssue> (input, 1);
}
[Test]
public void TestWhile3 ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod ()
{
IEnumerable<object> e;
int i;
object x;
while (true) {
if (i > 1) {
x = e.First ();
break;
}
}
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestWhile4 ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
IEnumerable<object> GetEnum () { }
void TestMethod (int i)
{
IEnumerable<object> e = GetEnum ();
var a1 = e.First ();
while ((e = GetEnum ()) != null) {
var a2 = e.First ();
}
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestDo ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
IEnumerable<object> GetEnum () { }
void TestMethod (int i)
{
IEnumerable<object> e = GetEnum ();
var a1 = e.First ();
do {
var a2 = e.First ();
} while ((e = GetEnum ()) != null);
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestDo2 ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
IEnumerable<object> GetEnum () { }
void TestMethod (int i)
{
IEnumerable<object> e = GetEnum ();
do {
var a2 = e.First ();
} while ((e = GetEnum ()) != null);
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestLambda ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod ()
{
IEnumerable<object> e;
Action a = () => {
var x = e.Count ();
var y = e.Count ();
};
var z = e.Count ();
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestLambda2 ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void Test (object a, object b) { }
void TestMethod ()
{
IEnumerable<object> e;
Action a = () => Test(e.First (), e.Count ());
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestLambda3 ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void Test (object a, Action b) { }
void TestMethod ()
{
IEnumerable<object> e;
Test(e.First (), () => e.Count ());
e = null;
var x = e.First ();
Action a = () => e.Count();
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestLambda4 ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void Test (object a, object b) { }
void TestMethod ()
{
IEnumerable<object> e;
Action a = () => Test(e.ToString (), e.ToString ());
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestConditionalExpression ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (int i)
{
IEnumerable<object> e;
var a = i > 0 ? e.First () : e.FirstOrDefault ();
Action b = () => i > 0 ? e.First () : e.FirstOrDefault ();
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestConditionalExpression2 ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (int i)
{
IEnumerable<object> e;
var a = i > 0 ? e.First () : new object ();
var b = e.First ();
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestConstantConditionalExpression ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (int i)
{
IEnumerable<object> e;
var a = 1 > 2 ? e.First () : new object ();
var b = e.First ();
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestAssignmentInConditionalExpression ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
IEnumerable<object> GetEnum () { }
void TestMethod (int i)
{
IEnumerable<object> e;
var x1 = e.First ();
var a = i > 0 ? e = GetEnum () : GetEnum ();
var x2 = e.First ();
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestAssignmentInConditionalExpression2 ()
{
var input = @"
using System;
using System.Collections.Generic;
using System.Linq;
class TestClass
{
IEnumerable<object> GetEnum () { }
void TestMethod (int i)
{
IEnumerable<object> e;
var x1 = e.First ();
var a = i > 0 ? e = GetEnum () : e = GetEnum ();
var x2 = e.First ();
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestAssignment ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (IEnumerable<object> e)
{
foreach (var x in e) ;
e = null;
var y = e.Count ();
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestAssignment2 ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (IEnumerable<object> e)
{
foreach (var x in e) ;
e = null;
var y = e.Count ();
e = null;
var a = e.First ();
var b = e.First ();
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestNoIssue ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void TestMethod (IEnumerable<object> e)
{
foreach (var x in e) ;
IEnumerable<object> e2;
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestExpression ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
int Test (params object[] args) { }
void TestMethod ()
{
IEnumerable<object> e = null;
var type = e.GetType();
var x = Test (e.First (), e.Count ());
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestExpression2 ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
int Test (params object[] args) { }
void TestMethod ()
{
IEnumerable<object> e = null;
var type = e.GetType();
var x = Test (e.First (), e = new objct[0], e.Count ());
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestOutArgument ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void Test (out IEnumerable<object> e)
{
e = null;
}
void TestMethod (IEnumerable<object> e)
{
foreach (var x in e) ;
Test (out e);
var y = e.Count ();
}
}";
Test<MultipleEnumerationIssue> (input, 0);
}
[Test]
public void TestOutArgument2 ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void Test (out IEnumerable<object> e)
{
e = null;
}
void TestMethod (IEnumerable<object> e)
{
foreach (var x in e) ;
Test (out e);
var y = e.Count ();
var z = e.Count ();
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
[Test]
public void TestOutArgument3 ()
{
var input = @"
using System.Collections.Generic;
using System.Linq;
class TestClass
{
void Test (object arg1, out IEnumerable<object> e, object arg2)
{
e = null;
}
void TestMethod (IEnumerable<object> e)
{
Test (e.First (), out e, e.First ());
}
}";
Test<MultipleEnumerationIssue> (input, 2);
}
}
}

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

@ -107,6 +107,7 @@ @@ -107,6 +107,7 @@
<Compile Include="CSharp\CodeIssues\LocalVariableHidesMemberIssueTests.cs" />
<Compile Include="CSharp\CodeIssues\LocalVariableNotUsedIssueTests.cs" />
<Compile Include="CSharp\CodeIssues\LocalVariableOnlyAssignedIssueTests.cs" />
<Compile Include="CSharp\CodeIssues\MultipleEnumerationIssueTests.cs" />
<Compile Include="CSharp\CodeIssues\ParameterHidesMemberIssueTests.cs" />
<Compile Include="CSharp\CodeIssues\ParameterNotUsedIssueTests.cs" />
<Compile Include="CSharp\CodeIssues\ParameterOnlyAssignedIssueTests.cs" />

Loading…
Cancel
Save