2
// AccessToClosureIssue.cs
5
// Mansheng Yang <lightyang0@gmail.com>
7
// Copyright (c) 2012 Mansheng Yang <lightyang0@gmail.com>
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
// of this software and associated documentation files (the "Software"), to deal
11
// in the Software without restriction, including without limitation the rights
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
// copies of the Software, and to permit persons to whom the Software is
14
// furnished to do so, subject to the following conditions:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
using System.Collections.Generic;
29
using ICSharpCode.NRefactory.CSharp.Analysis;
30
using ICSharpCode.NRefactory.Semantics;
31
using ICSharpCode.NRefactory.TypeSystem;
33
namespace ICSharpCode.NRefactory.CSharp.Refactoring
35
public abstract class AccessToClosureIssue : ICodeIssueProvider
37
ControlFlowGraphBuilder cfgBuilder = new ControlFlowGraphBuilder ();
42
protected AccessToClosureIssue (string title)
47
public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context)
49
var unit = context.RootNode as SyntaxTree;
51
return Enumerable.Empty<CodeIssue> ();
52
return new GatherVisitor (context, unit, this).GetIssues ();
55
protected virtual bool IsTargetVariable (IVariable variable)
60
protected abstract NodeKind GetNodeKind (AstNode node);
62
protected virtual bool CanReachModification (ControlFlowNode node, Statement start,
63
IDictionary<Statement, IList<Node>> modifications)
65
return node.NextStatement != null && node.NextStatement != start &&
66
modifications.ContainsKey (node.NextStatement);
69
protected abstract IEnumerable<CodeAction> GetFixes (BaseRefactoringContext context, Node env,
74
class GatherVisitor : GatherVisitorBase<AccessToClosureIssue>
78
public GatherVisitor (BaseRefactoringContext context, SyntaxTree unit,
79
AccessToClosureIssue issueProvider)
80
: base (context, issueProvider)
82
this.title = context.TranslateString (issueProvider.Title);
85
public override void VisitVariableInitializer (VariableInitializer variableInitializer)
87
var variableDecl = variableInitializer.Parent as VariableDeclarationStatement;
88
if (variableDecl != null)
89
CheckVariable (((LocalResolveResult)ctx.Resolve (variableInitializer)).Variable,
90
variableDecl.GetParent<Statement> ());
91
base.VisitVariableInitializer (variableInitializer);
94
public override void VisitForeachStatement (ForeachStatement foreachStatement)
96
CheckVariable (((LocalResolveResult)ctx.Resolve (foreachStatement.VariableNameToken)).Variable,
98
base.VisitForeachStatement (foreachStatement);
101
public override void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration)
103
var parent = parameterDeclaration.Parent;
104
Statement body = null;
105
if (parent is MethodDeclaration) {
106
body = ((MethodDeclaration)parent).Body;
107
} else if (parent is AnonymousMethodExpression) {
108
body = ((AnonymousMethodExpression)parent).Body;
109
} else if (parent is LambdaExpression) {
110
body = ((LambdaExpression)parent).Body as Statement;
111
} else if (parent is ConstructorDeclaration) {
112
body = ((ConstructorDeclaration)parent).Body;
113
} else if (parent is OperatorDeclaration) {
114
body = ((OperatorDeclaration)parent).Body;
117
var lrr = ctx.Resolve (parameterDeclaration) as LocalResolveResult;
119
CheckVariable (lrr.Variable, body);
121
base.VisitParameterDeclaration (parameterDeclaration);
124
void CheckVariable(IVariable variable, Statement env)
126
if (!IssueProvider.IsTargetVariable(variable))
129
var root = new Environment (env, env);
130
var envLookup = new Dictionary<AstNode, Environment> ();
131
envLookup [env] = root;
133
foreach (var result in ctx.FindReferences(env, variable)) {
134
AddNode(envLookup, new Node(result.Node, IssueProvider.GetNodeKind(result.Node)));
137
root.SortChildren ();
138
CollectIssues (root, variable.Name);
141
void CollectIssues (Environment env, string variableName)
143
IList<ControlFlowNode> cfg = null;
144
IDictionary<Statement, IList<Node>> modifications = null;
146
if (env.Body != null) {
147
cfg = IssueProvider.cfgBuilder.BuildControlFlowGraph (env.Body);
148
modifications = new Dictionary<Statement, IList<Node>> ();
149
foreach (var node in env.Children) {
150
if (node.Kind == NodeKind.Modification || node.Kind == NodeKind.ReferenceAndModification) {
152
if (!modifications.TryGetValue (node.ContainingStatement, out nodes))
153
modifications [node.ContainingStatement] = nodes = new List<Node> ();
159
foreach (var child in env.GetChildEnvironments ()) {
160
if (!child.IssueCollected && cfg != null &&
161
CanReachModification (cfg, child, modifications))
162
CollectAllIssues (child, variableName);
164
CollectIssues (child, variableName);
168
void CollectAllIssues (Environment env, string variableName)
170
var fixes = IssueProvider.GetFixes (ctx, env, variableName).ToArray ();
171
env.IssueCollected = true;
173
foreach (var child in env.Children) {
174
if (child is Environment) {
175
CollectAllIssues ((Environment)child, variableName);
177
if (child.Kind != NodeKind.Modification)
178
AddIssue (child.AstNode, title, fixes);
179
// stop marking references after the variable is modified in current environment
180
if (child.Kind != NodeKind.Reference)
186
void AddNode (IDictionary<AstNode, Environment> envLookup, Node node)
188
var astParent = node.AstNode.Parent;
189
var path = new List<AstNode> ();
190
while (astParent != null) {
192
if (envLookup.TryGetValue (astParent, out parent)) {
193
parent.Children.Add (node);
197
if (astParent is LambdaExpression) {
198
parent = new Environment (astParent, ((LambdaExpression)astParent).Body as Statement);
199
} else if (astParent is AnonymousMethodExpression) {
200
parent = new Environment (astParent, ((AnonymousMethodExpression)astParent).Body);
203
path.Add (astParent);
204
if (parent != null) {
205
foreach (var astNode in path)
206
envLookup [astNode] = parent;
209
parent.Children.Add (node);
212
astParent = astParent.Parent;
216
bool CanReachModification (IEnumerable<ControlFlowNode> cfg, Environment env,
217
IDictionary<Statement, IList<Node>> modifications)
219
if (modifications.Count == 0)
222
var start = env.ContainingStatement;
223
if (modifications.ContainsKey (start) &&
224
modifications [start].Any (v => v.AstNode.StartLocation > env.AstNode.EndLocation))
227
var stack = new Stack<ControlFlowNode> (cfg.Where (node => node.NextStatement == start));
228
var visitedNodes = new HashSet<ControlFlowNode> (stack);
229
while (stack.Count > 0) {
230
var node = stack.Pop ();
231
if (IssueProvider.CanReachModification (node, start, modifications))
233
foreach (var edge in node.Outgoing) {
234
if (visitedNodes.Add (edge.To))
235
stack.Push (edge.To);
247
protected enum NodeKind
251
ReferenceAndModification,
257
public AstNode AstNode
258
{ get; private set; }
261
{ get; private set; }
263
public Statement ContainingStatement
264
{ get; private set; }
266
public Node (AstNode astNode, NodeKind kind)
270
ContainingStatement = astNode.GetParent<Statement> ();
273
public virtual IEnumerable<Node> GetAllReferences ()
279
protected class Environment : Node
281
public Statement Body
282
{ get; private set; }
284
public bool IssueCollected
287
public List<Node> Children
288
{ get; private set; }
290
public Environment (AstNode astNode, Statement body)
291
: base (astNode, NodeKind.Environment)
294
Children = new List<Node> ();
297
public override IEnumerable<Node> GetAllReferences ()
299
return Children.SelectMany (child => child.GetAllReferences ());
302
public IEnumerable<Environment> GetChildEnvironments ()
304
return from child in Children
305
where child is Environment
306
select (Environment)child;
309
public void SortChildren ()
311
Children.Sort ((x, y) => x.AstNode.StartLocation.CompareTo(y.AstNode.StartLocation));
312
foreach (var env in GetChildEnvironments ())