8 changed files with 1471 additions and 0 deletions
@ -0,0 +1,329 @@
@@ -0,0 +1,329 @@
|
||||
//
|
||||
// AccessToClosureIssue.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.Analysis; |
||||
using ICSharpCode.NRefactory.CSharp.Resolver; |
||||
using ICSharpCode.NRefactory.Semantics; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.Refactoring |
||||
{ |
||||
public abstract class AccessToClosureIssue : ICodeIssueProvider |
||||
{ |
||||
static FindReferences refFinder = new FindReferences (); |
||||
static ControlFlowGraphBuilder cfgBuilder = new ControlFlowGraphBuilder (); |
||||
|
||||
public string Title |
||||
{ get; private set; } |
||||
|
||||
protected AccessToClosureIssue (string title) |
||||
{ |
||||
Title = title; |
||||
} |
||||
|
||||
public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context) |
||||
{ |
||||
var unit = context.RootNode as CompilationUnit; |
||||
if (unit == null) |
||||
return Enumerable.Empty<CodeIssue> (); |
||||
return new GatherVisitor (context, unit, this).GetIssues (); |
||||
} |
||||
|
||||
protected virtual bool IsTargetVariable (IVariable variable) |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
protected abstract NodeKind GetNodeKind (AstNode node); |
||||
|
||||
protected virtual bool CanReachModification (ControlFlowNode node, Statement start, |
||||
IDictionary<Statement, IList<Node>> modifications) |
||||
{ |
||||
return node.NextStatement != null && node.NextStatement != start && |
||||
modifications.ContainsKey (node.NextStatement); |
||||
} |
||||
|
||||
protected abstract IEnumerable<CodeAction> GetFixes (BaseRefactoringContext context, Node env, |
||||
string variableName, AstType variableType); |
||||
|
||||
#region GatherVisitor
|
||||
|
||||
class GatherVisitor : GatherVisitorBase |
||||
{ |
||||
CompilationUnit unit; |
||||
string title; |
||||
AccessToClosureIssue issueProvider; |
||||
|
||||
public GatherVisitor (BaseRefactoringContext context, CompilationUnit unit, |
||||
AccessToClosureIssue issueProvider) |
||||
: base (context) |
||||
{ |
||||
this.title = context.TranslateString (issueProvider.Title); |
||||
this.unit = unit; |
||||
this.issueProvider = issueProvider; |
||||
} |
||||
|
||||
public override void VisitVariableInitializer (VariableInitializer variableInitializer) |
||||
{ |
||||
var variableDecl = variableInitializer.Parent as VariableDeclarationStatement; |
||||
if (variableDecl == null) |
||||
return; |
||||
|
||||
CheckVariable (((LocalResolveResult)ctx.Resolve (variableInitializer)).Variable, |
||||
variableDecl.Type, |
||||
variableDecl.GetParent<Statement> ()); |
||||
base.VisitVariableInitializer (variableInitializer); |
||||
} |
||||
|
||||
public override void VisitForeachStatement (ForeachStatement foreachStatement) |
||||
{ |
||||
CheckVariable (((LocalResolveResult)ctx.Resolve (foreachStatement.VariableNameToken)).Variable, |
||||
foreachStatement.VariableType, |
||||
foreachStatement); |
||||
base.VisitForeachStatement (foreachStatement); |
||||
} |
||||
|
||||
public override void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) |
||||
{ |
||||
var parent = parameterDeclaration.Parent; |
||||
Statement body = null; |
||||
if (parent is MethodDeclaration) { |
||||
body = ((MethodDeclaration)parent).Body; |
||||
} else if (parent is AnonymousMethodExpression) { |
||||
body = ((AnonymousMethodExpression)parent).Body; |
||||
} else if (parent is LambdaExpression) { |
||||
body = ((LambdaExpression)parent).Body as Statement; |
||||
} |
||||
if (body != null) |
||||
CheckVariable (((LocalResolveResult)ctx.Resolve (parameterDeclaration)).Variable, |
||||
parameterDeclaration.Type, body); |
||||
base.VisitParameterDeclaration (parameterDeclaration); |
||||
} |
||||
|
||||
void FindLocalReferences (IVariable variable, FoundReferenceCallback callback) |
||||
{ |
||||
refFinder.FindLocalReferences (variable, ctx.ParsedFile, unit, ctx.Compilation, callback, |
||||
ctx.CancellationToken); |
||||
} |
||||
|
||||
void CheckVariable (IVariable variable, AstType type, Statement env) |
||||
{ |
||||
if (!issueProvider.IsTargetVariable (variable)) |
||||
return; |
||||
|
||||
var root = new Environment (env, env); |
||||
var envLookup = new Dictionary<AstNode, Environment> (); |
||||
envLookup [env] = root; |
||||
|
||||
FindLocalReferences (variable, (astNode, resolveResult) => |
||||
AddNode (envLookup, new Node (astNode, issueProvider.GetNodeKind (astNode)))); |
||||
|
||||
root.SortChildren (); |
||||
CollectIssues (root, variable.Name, type); |
||||
} |
||||
|
||||
void CollectIssues (Environment env, string variableName, AstType variableType) |
||||
{ |
||||
IList<ControlFlowNode> cfg = null; |
||||
IDictionary<Statement, IList<Node>> modifications = null; |
||||
|
||||
if (env.Body != null) { |
||||
cfg = cfgBuilder.BuildControlFlowGraph (env.Body); |
||||
modifications = new Dictionary<Statement, IList<Node>> (); |
||||
foreach (var node in env.Children) { |
||||
if (node.Kind == NodeKind.Modification || node.Kind == NodeKind.ReferenceAndModification) { |
||||
IList<Node> nodes; |
||||
if (!modifications.TryGetValue (node.ContainingStatement, out nodes)) |
||||
modifications [node.ContainingStatement] = nodes = new List<Node> (); |
||||
nodes.Add (node); |
||||
} |
||||
} |
||||
} |
||||
|
||||
foreach (var child in env.GetChildEnvironments ()) { |
||||
if (!child.IssueCollected && cfg != null && |
||||
CanReachModification (cfg, child, modifications)) |
||||
CollectAllIssues (child, variableName, variableType); |
||||
|
||||
CollectIssues (child, variableName, variableType); |
||||
} |
||||
} |
||||
|
||||
void CollectAllIssues (Environment env, string variableName, AstType variableType) |
||||
{ |
||||
var fixes = issueProvider.GetFixes (ctx, env, variableName, variableType).ToArray (); |
||||
env.IssueCollected = true; |
||||
|
||||
foreach (var child in env.Children) { |
||||
if (child is Environment) { |
||||
CollectAllIssues ((Environment)child, variableName, variableType); |
||||
} else { |
||||
if (child.Kind != NodeKind.Modification) |
||||
AddIssue (child.AstNode, title, fixes); |
||||
// stop marking references after the variable is modified in current environment
|
||||
if (child.Kind != NodeKind.Reference) |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void AddNode (IDictionary<AstNode, Environment> envLookup, Node node) |
||||
{ |
||||
var astParent = node.AstNode.Parent; |
||||
var path = new List<AstNode> (); |
||||
while (astParent != null) { |
||||
Environment parent; |
||||
if (envLookup.TryGetValue (astParent, out parent)) { |
||||
parent.Children.Add (node); |
||||
return; |
||||
} |
||||
|
||||
if (astParent is LambdaExpression) { |
||||
parent = new Environment (astParent, ((LambdaExpression)astParent).Body as Statement); |
||||
} else if (astParent is AnonymousMethodExpression) { |
||||
parent = new Environment (astParent, ((AnonymousMethodExpression)astParent).Body); |
||||
} |
||||
|
||||
path.Add (astParent); |
||||
if (parent != null) { |
||||
foreach (var astNode in path) |
||||
envLookup [astNode] = parent; |
||||
path.Clear (); |
||||
|
||||
parent.Children.Add (node); |
||||
node = parent; |
||||
} |
||||
astParent = astParent.Parent; |
||||
} |
||||
} |
||||
|
||||
bool CanReachModification (IEnumerable<ControlFlowNode> cfg, Environment env, |
||||
IDictionary<Statement, IList<Node>> modifications) |
||||
{ |
||||
if (modifications.Count == 0) |
||||
return false; |
||||
|
||||
var start = env.ContainingStatement; |
||||
if (modifications.ContainsKey (start) && |
||||
modifications [start].Any (v => v.AstNode.StartLocation > env.AstNode.EndLocation)) |
||||
return true; |
||||
|
||||
var stack = new Stack<ControlFlowNode> (cfg.Where (node => node.NextStatement == start)); |
||||
var visitedNodes = new HashSet<ControlFlowNode> (stack); |
||||
while (stack.Count > 0) { |
||||
var node = stack.Pop (); |
||||
if (issueProvider.CanReachModification (node, start, modifications)) |
||||
return true; |
||||
foreach (var edge in node.Outgoing) { |
||||
if (visitedNodes.Add (edge.To)) |
||||
stack.Push (edge.To); |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
#region Node
|
||||
|
||||
protected enum NodeKind |
||||
{ |
||||
Reference, |
||||
Modification, |
||||
ReferenceAndModification, |
||||
Environment, |
||||
} |
||||
|
||||
protected class Node |
||||
{ |
||||
public AstNode AstNode |
||||
{ get; private set; } |
||||
|
||||
public NodeKind Kind |
||||
{ get; private set; } |
||||
|
||||
public Statement ContainingStatement |
||||
{ get; private set; } |
||||
|
||||
public Node (AstNode astNode, NodeKind kind) |
||||
{ |
||||
AstNode = astNode; |
||||
Kind = kind; |
||||
ContainingStatement = astNode.GetParent<Statement> (); |
||||
} |
||||
|
||||
public virtual IEnumerable<Node> GetAllReferences () |
||||
{ |
||||
yield return this; |
||||
} |
||||
} |
||||
|
||||
protected class Environment : Node |
||||
{ |
||||
public Statement Body |
||||
{ get; private set; } |
||||
|
||||
public bool IssueCollected |
||||
{ get; set; } |
||||
|
||||
public List<Node> Children |
||||
{ get; private set; } |
||||
|
||||
public Environment (AstNode astNode, Statement body) |
||||
: base (astNode, NodeKind.Environment) |
||||
{ |
||||
Body = body; |
||||
Children = new List<Node> (); |
||||
} |
||||
|
||||
public override IEnumerable<Node> GetAllReferences () |
||||
{ |
||||
return Children.SelectMany (child => child.GetAllReferences ()); |
||||
} |
||||
|
||||
public IEnumerable<Environment> GetChildEnvironments () |
||||
{ |
||||
return from child in Children |
||||
where child is Environment |
||||
select (Environment)child; |
||||
} |
||||
|
||||
public void SortChildren () |
||||
{ |
||||
Children.Sort ((x, y) => x.AstNode.StartLocation.CompareTo(y.AstNode.StartLocation)); |
||||
foreach (var env in GetChildEnvironments ()) |
||||
env.SortChildren (); |
||||
} |
||||
} |
||||
|
||||
#endregion
|
||||
} |
||||
} |
||||
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// AccessToDisposedClosureIssue.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.Analysis; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.Refactoring |
||||
{ |
||||
|
||||
[IssueDescription ("Access to disposed closure variable", |
||||
Description = "Access to closure variable from anonymous method when the variable is" + |
||||
" disposed externally", |
||||
Category = IssueCategories.CodeQualityIssues, |
||||
Severity = Severity.Warning, |
||||
IssueMarker = IssueMarker.Underline)] |
||||
public class AccessToDisposedClosureIssue : AccessToClosureIssue |
||||
{ |
||||
public AccessToDisposedClosureIssue () |
||||
: base ("Access to disposed closure") |
||||
{ |
||||
} |
||||
|
||||
protected override bool IsTargetVariable (IVariable variable) |
||||
{ |
||||
return variable.Type.GetAllBaseTypeDefinitions ().Any (t => t.KnownTypeCode == KnownTypeCode.IDisposable); |
||||
} |
||||
|
||||
protected override NodeKind GetNodeKind (AstNode node) |
||||
{ |
||||
if (node.Parent is UsingStatement) |
||||
return NodeKind.ReferenceAndModification; |
||||
|
||||
if (node.Parent is VariableDeclarationStatement && node.Parent.Parent is UsingStatement) |
||||
return NodeKind.Modification; |
||||
|
||||
var memberRef = node.Parent as MemberReferenceExpression; |
||||
if (memberRef != null && memberRef.Parent is InvocationExpression && memberRef.MemberName == "Dispose") |
||||
return NodeKind.ReferenceAndModification; |
||||
|
||||
return NodeKind.Reference; |
||||
} |
||||
|
||||
protected override bool CanReachModification (ControlFlowNode node, Statement start, |
||||
IDictionary<Statement, IList<Node>> modifications) |
||||
{ |
||||
if (base.CanReachModification (node, start, modifications)) |
||||
return true; |
||||
|
||||
if (node.NextStatement != start) { |
||||
var usingStatement = node.PreviousStatement as UsingStatement; |
||||
if (usingStatement != null) { |
||||
if (modifications.ContainsKey(usingStatement)) |
||||
return true; |
||||
if (usingStatement.ResourceAcquisition is Statement && |
||||
modifications.ContainsKey ((Statement)usingStatement.ResourceAcquisition)) |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
protected override IEnumerable<CodeAction> GetFixes (BaseRefactoringContext context, Node env, |
||||
string variableName, AstType variableType) |
||||
{ |
||||
yield break; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,113 @@
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// AccessToModifiedClosureIssue.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; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.Refactoring |
||||
{ |
||||
[IssueDescription ("Access to modified closure variable", |
||||
Description = "Access to closure variable from anonymous method when the variable is modified " + |
||||
"externally", |
||||
Category = IssueCategories.CodeQualityIssues, |
||||
Severity = Severity.Warning, |
||||
IssueMarker = IssueMarker.Underline)] |
||||
public class AccessToModifiedClosureIssue : AccessToClosureIssue |
||||
{ |
||||
public AccessToModifiedClosureIssue () |
||||
: base ("Access to modified closure") |
||||
{ |
||||
} |
||||
|
||||
protected override NodeKind GetNodeKind (AstNode node) |
||||
{ |
||||
var assignment = node.GetParent<AssignmentExpression> (); |
||||
if (assignment != null && assignment.Left == node) { |
||||
if (assignment.Operator == AssignmentOperatorType.Assign) |
||||
return NodeKind.Modification; |
||||
return NodeKind.ReferenceAndModification; |
||||
} |
||||
var unaryExpr = node.GetParent<UnaryOperatorExpression> (); |
||||
if (unaryExpr != null && unaryExpr.Expression == node && |
||||
(unaryExpr.Operator == UnaryOperatorType.Increment || |
||||
unaryExpr.Operator == UnaryOperatorType.PostIncrement || |
||||
unaryExpr.Operator == UnaryOperatorType.Decrement || |
||||
unaryExpr.Operator == UnaryOperatorType.PostDecrement)) { |
||||
return NodeKind.ReferenceAndModification; |
||||
} |
||||
if (node.Parent is ForeachStatement) |
||||
return NodeKind.Modification; |
||||
|
||||
return NodeKind.Reference; |
||||
} |
||||
|
||||
protected override IEnumerable<CodeAction> GetFixes (BaseRefactoringContext context, Node env, |
||||
string variableName, AstType variableType) |
||||
{ |
||||
var containingStatement = env.ContainingStatement; |
||||
|
||||
// we don't give a fix for these cases since the general fix may not work
|
||||
// lambda in while/do-while/for condition
|
||||
if (containingStatement is WhileStatement || containingStatement is DoWhileStatement || |
||||
containingStatement is ForStatement) |
||||
yield break; |
||||
// lambda in for initializer/iterator
|
||||
if (containingStatement.Parent is ForStatement && |
||||
((ForStatement)containingStatement.Parent).EmbeddedStatement != containingStatement) |
||||
yield break; |
||||
|
||||
Action<Script> action = script => |
||||
{ |
||||
var newName = LocalVariableNamePicker.PickSafeName (containingStatement.GetParent<MethodDeclaration> (), |
||||
Enumerable.Range (1, 100).Select (i => variableName + i)); |
||||
var variableDecl = new VariableDeclarationStatement (variableType.Clone (), newName, |
||||
new IdentifierExpression (variableName)); |
||||
|
||||
if (containingStatement.Parent is BlockStatement || containingStatement.Parent is SwitchSection) { |
||||
script.InsertBefore (containingStatement, variableDecl); |
||||
} else { |
||||
var offset = script.GetCurrentOffset (containingStatement.StartLocation); |
||||
script.InsertBefore (containingStatement, variableDecl); |
||||
script.InsertText (offset, "{"); |
||||
script.InsertText (script.GetCurrentOffset (containingStatement.EndLocation), "}"); |
||||
script.FormatText (containingStatement.Parent); |
||||
} |
||||
|
||||
var textNodes = new List<AstNode> (); |
||||
textNodes.Add (variableDecl.Variables.First ().NameToken); |
||||
|
||||
foreach (var reference in env.GetAllReferences ()) { |
||||
var identifier = new IdentifierExpression (newName); |
||||
script.Replace (reference.AstNode, identifier); |
||||
textNodes.Add (identifier); |
||||
} |
||||
script.Link (textNodes.ToArray ()); |
||||
}; |
||||
yield return new CodeAction (context.TranslateString ("Copy to local variable"), action); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// NamePicker.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; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.Refactoring |
||||
{ |
||||
public static class LocalVariableNamePicker |
||||
{ |
||||
public static string PickSafeName (MethodDeclaration method, IEnumerable<string> candidates) |
||||
{ |
||||
var existingNames = new VariableNameCollector ().Collect (method); |
||||
return candidates.FirstOrDefault (name => !existingNames.Contains (name)); |
||||
} |
||||
|
||||
class VariableNameCollector : DepthFirstAstVisitor |
||||
{ |
||||
private ISet<string> variableNames = new HashSet<string> (); |
||||
|
||||
public ISet<string> Collect (MethodDeclaration methodDecl) |
||||
{ |
||||
variableNames.Clear (); |
||||
methodDecl.AcceptVisitor (this); |
||||
return variableNames; |
||||
} |
||||
|
||||
public override void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) |
||||
{ |
||||
variableNames.Add (parameterDeclaration.Name); |
||||
base.VisitParameterDeclaration (parameterDeclaration); |
||||
} |
||||
|
||||
public override void VisitVariableInitializer (VariableInitializer variableInitializer) |
||||
{ |
||||
variableNames.Add (variableInitializer.Name); |
||||
base.VisitVariableInitializer (variableInitializer); |
||||
} |
||||
|
||||
public override void VisitForeachStatement (ForeachStatement foreachStatement) |
||||
{ |
||||
variableNames.Add (foreachStatement.VariableName); |
||||
base.VisitForeachStatement (foreachStatement); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,106 @@
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// AccessToDisposedClosureTests.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.CodeActions; |
||||
using ICSharpCode.NRefactory.CSharp.Refactoring; |
||||
using NUnit.Framework; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.CodeIssues |
||||
{ |
||||
public class AccessToDisposedClosureTests : InspectionActionTestBase |
||||
{ |
||||
void Test (string input, int issueCount) |
||||
{ |
||||
TestRefactoringContext context; |
||||
var issues = GetIssues (new AccessToDisposedClosureIssue (), input, out context); |
||||
Assert.AreEqual (issueCount, issues.Count); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestUsing () |
||||
{ |
||||
string input = @"
|
||||
using System; |
||||
class TestClass : IDisposable |
||||
{ |
||||
public void Dispose () { } |
||||
} |
||||
class TestClass2 |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
using (var x = new TestClass ()) { |
||||
Action a = () => x.ToString (); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestUsing2 () |
||||
{ |
||||
string input = @"
|
||||
using System; |
||||
class TestClass : IDisposable |
||||
{ |
||||
public void Dispose () { } |
||||
} |
||||
class TestClass2 |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
var x = new TestClass (); |
||||
using (x) { |
||||
Action a = () => x.ToString (); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestDispose () |
||||
{ |
||||
string input = @"
|
||||
using System; |
||||
class TestClass : IDisposable |
||||
{ |
||||
public void Dispose () { } |
||||
} |
||||
class TestClass2 |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
var x = new TestClass (); |
||||
Action a = () => x.ToString (); |
||||
x.Dispose(); |
||||
} |
||||
}";
|
||||
Test (input, 1); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,753 @@
@@ -0,0 +1,753 @@
|
||||
//
|
||||
// AccessToModifiedClosureTests.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.CodeActions; |
||||
using ICSharpCode.NRefactory.CSharp.Refactoring; |
||||
using NUnit.Framework; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.CodeIssues |
||||
{ |
||||
[TestFixture] |
||||
public class AccessToModifiedClosureTests : InspectionActionTestBase |
||||
{ |
||||
static void Test(string input, int issueCount, string output = null, int fixIndex = -1) |
||||
{ |
||||
TestRefactoringContext context; |
||||
var issues = GetIssues (new AccessToModifiedClosureIssue (), input, out context); |
||||
Assert.AreEqual (issueCount, issues.Count); |
||||
if (issueCount > 0) { |
||||
if (output != null) { |
||||
if (fixIndex == -1) |
||||
CheckFix (context, issues, output); |
||||
else |
||||
CheckFix (context, issues [fixIndex], output); |
||||
} else { |
||||
foreach (var issue in issues) |
||||
Assert.AreEqual (0, issue.Actions.Count); |
||||
} |
||||
} |
||||
} |
||||
|
||||
[Test] |
||||
public void TestForeachVariable () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
System.Func<int, int> f; |
||||
foreach (var i in a) |
||||
f = new System.Func<int, int> (x => x + i); |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
System.Func<int, int> f; |
||||
foreach (var i in a) { |
||||
var i1 = i; |
||||
f = new System.Func<int, int> (x => x + i1); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestFor () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
for (int i = 0; i < 10; i++) { |
||||
var f = new System.Func<int, int> (x => x + i); |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
for (int i = 0; i < 10; i++) { |
||||
int i1 = i; |
||||
var f = new System.Func<int, int> (x => x + i1); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestForInitializer () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int j) |
||||
{ |
||||
int i; |
||||
for (i = 0; j < 10; j++) { |
||||
var f = new System.Func<int, int> (x => x + i); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 0); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestForBody () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
for (int i = 0; i < 10;) { |
||||
var f = new System.Func<int, int> (delegate (int x) { return x + i; }); |
||||
i++; |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
for (int i = 0; i < 10;) { |
||||
int i1 = i; |
||||
var f = new System.Func<int, int> (delegate (int x) { return x + i1; }); |
||||
i++; |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestWhileBody () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
while (i < 10) { |
||||
i++; |
||||
var f = new System.Func<int, int> (x => x + i); |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
while (i < 10) { |
||||
i++; |
||||
int i1 = i; |
||||
var f = new System.Func<int, int> (x => x + i1); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestWhileCondition () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
while (i++ < 10) { |
||||
var f = new System.Func<int, int> (delegate (int x) { return x + i; }); |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
while (i++ < 10) { |
||||
int i1 = i; |
||||
var f = new System.Func<int, int> (delegate (int x) { return x + i1; }); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestDoWhileBody () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
do { |
||||
i += 1; |
||||
var f = new System.Func<int, int> (x => x + i); |
||||
} while (i < 10); |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
do { |
||||
i += 1; |
||||
int i1 = i; |
||||
var f = new System.Func<int, int> (x => x + i1); |
||||
} while (i < 10); |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestDoWhileCondition () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
while (i++ < 10) { |
||||
var f = new System.Func<int, int> (x => x + i); |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
while (i++ < 10) { |
||||
int i1 = i; |
||||
var f = new System.Func<int, int> (x => x + i1); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestMultipleLambdas () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
foreach (var i in a) { |
||||
var f = new System.Func<int, int> (x => x + i); |
||||
var f2 = new System.Func<int, bool> (x => x + i > 10); |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
foreach (var i in a) { |
||||
var i1 = i; |
||||
var f = new System.Func<int, int> (x => x + i1); |
||||
var i1 = i; |
||||
var f2 = new System.Func<int, bool> (x => x + i1 > 10); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 2, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestFixAllInLambda () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] array) |
||||
{ |
||||
foreach (var i in array) { |
||||
var f = new System.Func<int, int> (x => { |
||||
int a = i + 1; |
||||
int b = i + 2; |
||||
return a + b + i; |
||||
}); |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] array) |
||||
{ |
||||
foreach (var i in array) { |
||||
var i1 = i; |
||||
var f = new System.Func<int, int> (x => { |
||||
int a = i1 + 1; |
||||
int b = i1 + 2; |
||||
return a + b + i1; |
||||
}); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 3, output, 0); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestModifiedInLambda () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] array) |
||||
{ |
||||
foreach (var i in array) { |
||||
var f = new System.Func<int, int> (x => { |
||||
i = 0; |
||||
int a = i + 1; |
||||
int b = i + 2; |
||||
return a + b + i; |
||||
}); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 0); |
||||
input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] array) |
||||
{ |
||||
foreach (var i in array) { |
||||
var f = new System.Func<int, int> (x => { |
||||
i++; |
||||
int a = i + 1; |
||||
i = 3; |
||||
int b = i + 2; |
||||
return a + b + i; |
||||
}); |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] array) |
||||
{ |
||||
foreach (var i in array) { |
||||
var i1 = i; |
||||
var f = new System.Func<int, int> (x => { |
||||
i1++; |
||||
int a = i1 + 1; |
||||
i1 = 3; |
||||
int b = i1 + 2; |
||||
return a + b + i1; |
||||
}); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestNestedLambda () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
foreach (var i in a) { |
||||
var f = new System.Func<int, int> (x => { |
||||
var f2 = new System.Func<int, int> (y => y - i); |
||||
return f2(x) + i; |
||||
}); |
||||
} |
||||
} |
||||
}";
|
||||
var output1 = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
foreach (var i in a) { |
||||
var i1 = i; |
||||
var f = new System.Func<int, int> (x => { |
||||
var f2 = new System.Func<int, int> (y => y - i1); |
||||
return f2(x) + i1; |
||||
}); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 2, output1, 1); |
||||
var output2 = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
foreach (var i in a) { |
||||
var f = new System.Func<int, int> (x => { |
||||
var i1 = i; |
||||
var f2 = new System.Func<int, int> (y => y - i1); |
||||
return f2(x) + i; |
||||
}); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 2, output2, 0); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestMultipleVariables () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
foreach (var i in a) { |
||||
foreach (var j in a) { |
||||
var f = new System.Func<int, int> (x => x + i + j); |
||||
} |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
foreach (var i in a) { |
||||
foreach (var j in a) { |
||||
var i1 = i; |
||||
var j1 = j; |
||||
var f = new System.Func<int, int> (x => x + i1 + j1); |
||||
} |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 2, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestModificationAfterLambdaDecl () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
System.Func<int, int> f = x => i + x; |
||||
i += 1; |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
int i1 = i; |
||||
System.Func<int, int> f = x => i1 + x; |
||||
i += 1; |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestModificationBeforeLambdaDecl () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
i += 1; |
||||
System.Func<int, int> f = x => i + x; |
||||
} |
||||
}";
|
||||
Test (input, 0); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestUnreachable () |
||||
{ |
||||
var returnCase = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
System.Func<int, int> f = x => i + x; |
||||
return; |
||||
i += 1; |
||||
} |
||||
}";
|
||||
var ifCase = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
System.Func<int, int> f; |
||||
if (i > 0) |
||||
f = x => i + x; |
||||
else |
||||
i += 1; |
||||
} |
||||
}";
|
||||
Test (returnCase, 0); |
||||
Test (ifCase, 0); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestParameter () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int i) |
||||
{ |
||||
System.Func<int, int> f = x => i + x; |
||||
i += 1; |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int i) |
||||
{ |
||||
int i1 = i; |
||||
System.Func<int, int> f = x => i1 + x; |
||||
i += 1; |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestModificationInSameStatement () |
||||
{ |
||||
var input1 = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod2 (int b, System.Func<int, int> a) |
||||
{ |
||||
TestMethod2 (b++, c => c + b); |
||||
} |
||||
}";
|
||||
var input2 = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod3 (System.Func<int, int> a, int b) |
||||
{ |
||||
TestMethod3 (c => c + b, b++); |
||||
} |
||||
}";
|
||||
var output2 = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod3 (System.Func<int, int> a, int b) |
||||
{ |
||||
int b1 = b; |
||||
TestMethod3 (c => c + b1, b++); |
||||
} |
||||
}";
|
||||
Test (input1, 0); |
||||
Test (input2, 1, output2); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestInsertion () |
||||
{ |
||||
var input1 = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
System.Func<int, int> f = null; |
||||
if ((f = x => x + i) != null) |
||||
i++; |
||||
} |
||||
}";
|
||||
var output1 = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
int i = 0; |
||||
System.Func<int, int> f = null; |
||||
int i1 = i; |
||||
if ((f = x => x + i1) != null) |
||||
i++; |
||||
} |
||||
}";
|
||||
Test (input1, 1, output1); |
||||
|
||||
var input2 = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int k) |
||||
{ |
||||
int i = 0; |
||||
System.Func<int, int> f = null; |
||||
switch (k) { |
||||
default: |
||||
f = x => x + i; |
||||
break; |
||||
} |
||||
i++; |
||||
} |
||||
}";
|
||||
var output2 = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int k) |
||||
{ |
||||
int i = 0; |
||||
System.Func<int, int> f = null; |
||||
switch (k) { |
||||
default: |
||||
int i1 = i; |
||||
f = x => x + i1; |
||||
break; |
||||
} |
||||
i++; |
||||
} |
||||
}";
|
||||
Test (input2, 1, output2); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestLambdaInLoopCondition () |
||||
{ |
||||
var forCase = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
System.Func<int, int> f = null; |
||||
for (int i = 0; i < 10 && ((f = x => x + i) != null); i++) { |
||||
} |
||||
} |
||||
}";
|
||||
var whileCase = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
System.Func<int, int> f = null; |
||||
int i = 0; |
||||
while (i < 10 && (f = x => x + i) != null) i++; |
||||
} |
||||
}";
|
||||
var doWhileCase = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
System.Func<int, int> f = null; |
||||
int i = 0; |
||||
do { i++; } while (i < 10 && (f = x => x + i) != null); |
||||
} |
||||
}";
|
||||
Test (forCase, 1); |
||||
Test (whileCase, 1); |
||||
Test (doWhileCase, 1); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestLambdaInForIterator () |
||||
{ |
||||
|
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod () |
||||
{ |
||||
System.Func<int, int> f = null; |
||||
for (int i = 0; i < 10; i++, f = x => x + i) { |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestExistingVariableName () |
||||
{ |
||||
var input = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
foreach (var i in a) { |
||||
int i1; |
||||
var f = new System.Func<int, int> (x => x + i); |
||||
} |
||||
} |
||||
}";
|
||||
var output = @"
|
||||
class TestClass |
||||
{ |
||||
void TestMethod (int[] a) |
||||
{ |
||||
foreach (var i in a) { |
||||
int i1; |
||||
var i2 = i; |
||||
var f = new System.Func<int, int> (x => x + i2); |
||||
} |
||||
} |
||||
}";
|
||||
Test (input, 1, output); |
||||
} |
||||
|
||||
} |
||||
} |
||||
Loading…
Reference in new issue