5 changed files with 1576 additions and 0 deletions
@ -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; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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
|
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue