Browse Source

Merge pull request #116 from riviti/performance

Performance enhancements
Mike Krüger 13 years ago
parent
commit
063a72c685
  1. 2
      ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  2. 10
      ICSharpCode.NRefactory.CSharp/Refactoring/BaseRefactoringContext.cs
  3. 19
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AccessToClosureIssues/AccessToClosureIssue.cs
  4. 48
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ForControlVariableNotModifiedIssue.cs
  5. 11
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/FormatStringIssues/FormatStringIssue.cs
  6. 6
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/IncorrectExceptionParameterOrderingIssue.cs
  7. 68
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/MultipleEnumerationIssue.cs
  8. 24
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/OptionalParameterCouldBeSkippedIssue.cs
  9. 57
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantAssignmentIssue.cs
  10. 17
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantToStringIssue.cs
  11. 5
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ReferenceEqualsCalledWithValueTypeIssue.cs
  12. 19
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ValueParameterUnusedIssue.cs
  13. 26
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/LocalVariableNotUsedIssue.cs
  14. 18
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/ParameterNotUsedIssue.cs
  15. 60
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/VariableNotUsedIssue.cs
  16. 11
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableOnlyAssignedIssues/LocalVariableOnlyAssignedIssue.cs
  17. 27
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableOnlyAssignedIssues/ParameterOnlyAssignedIssue.cs
  18. 69
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableOnlyAssignedIssues/VariableOnlyAssignedIssue.cs
  19. 157
      ICSharpCode.NRefactory.CSharp/Refactoring/LocalReferenceFinder.cs

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

@ -324,7 +324,6 @@ @@ -324,7 +324,6 @@
<Compile Include="Refactoring\CodeIssues\VariableNotUsedIssues\LocalVariableNotUsedIssue.cs" />
<Compile Include="Refactoring\CodeIssues\VariableNotUsedIssues\ParameterNotUsedIssue.cs" />
<Compile Include="Refactoring\CodeIssues\TypeParameterNotUsedIssue.cs" />
<Compile Include="Refactoring\CodeIssues\VariableNotUsedIssues\VariableNotUsedIssue.cs" />
<Compile Include="Refactoring\CodeIssues\VariableOnlyAssignedIssues\ParameterOnlyAssignedIssue.cs" />
<Compile Include="Refactoring\CodeIssues\VariableOnlyAssignedIssues\VariableOnlyAssignedIssue.cs" />
<Compile Include="Refactoring\DocumentScript.cs" />
@ -503,6 +502,7 @@ @@ -503,6 +502,7 @@
<Compile Include="Refactoring\CodeIssues\ParameterCanBeDemotedIssue\SupportsIndexingCriterion.cs" />
<Compile Include="Refactoring\CodeIssues\RedundantWhereWithPredicateIssue.cs" />
<Compile Include="Refactoring\CodeActions\ConvertToInitializer\AccessPath.cs" />
<Compile Include="Refactoring\LocalReferenceFinder.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">

10
ICSharpCode.NRefactory.CSharp/Refactoring/BaseRefactoringContext.cs

@ -36,6 +36,7 @@ using ICSharpCode.NRefactory.Editor; @@ -36,6 +36,7 @@ using ICSharpCode.NRefactory.Editor;
using System.ComponentModel.Design;
using ICSharpCode.NRefactory.CSharp.Analysis;
using ICSharpCode.NRefactory.Utils;
using System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
@ -87,6 +88,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -87,6 +88,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
this.resolver = resolver;
this.cancellationToken = cancellationToken;
this.referenceFinder = new LocalReferenceFinder(resolver);
}
@ -161,6 +163,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -161,6 +163,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
return new CompositeFormatStringParser().Parse(source);
}
LocalReferenceFinder referenceFinder;
public IList<ReferenceResult> FindReferences(AstNode rootNode, IVariable variable)
{
return referenceFinder.FindReferences(rootNode, variable);
}
#endregion
/// <summary>

19
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AccessToClosureIssues/AccessToClosureIssue.cs

@ -27,7 +27,6 @@ @@ -27,7 +27,6 @@
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;
@ -35,7 +34,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -35,7 +34,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public abstract class AccessToClosureIssue : ICodeIssueProvider
{
static FindReferences refFinder = new FindReferences ();
static ControlFlowGraphBuilder cfgBuilder = new ControlFlowGraphBuilder ();
public string Title
@ -75,7 +73,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -75,7 +73,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
class GatherVisitor : GatherVisitorBase
{
SyntaxTree unit;
string title;
AccessToClosureIssue issueProvider;
@ -84,7 +81,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -84,7 +81,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
: base (context)
{
this.title = context.TranslateString (issueProvider.Title);
this.unit = unit;
this.issueProvider = issueProvider;
}
@ -124,23 +120,18 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -124,23 +120,18 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
base.VisitParameterDeclaration (parameterDeclaration);
}
void FindLocalReferences (IVariable variable, FoundReferenceCallback callback)
void CheckVariable(IVariable variable, Statement env)
{
refFinder.FindLocalReferences (variable, ctx.UnresolvedFile, unit, ctx.Compilation, callback,
ctx.CancellationToken);
}
void CheckVariable (IVariable variable, Statement env)
{
if (!issueProvider.IsTargetVariable (variable))
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))));
foreach (var result in ctx.FindReferences(env, variable)) {
AddNode(envLookup, new Node(result.Node, issueProvider.GetNodeKind(result.Node)));
}
root.SortChildren ();
CollectIssues (root, variable.Name);

48
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ForControlVariableNotModifiedIssue.cs

@ -26,7 +26,6 @@ @@ -26,7 +26,6 @@
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.PatternMatching;
using ICSharpCode.NRefactory.Semantics;
@ -39,24 +38,16 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -39,24 +38,16 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
IssueMarker = IssueMarker.Underline)]
public class ForControlVariableNotModifiedIssue : ICodeIssueProvider
{
static FindReferences refFinder = new FindReferences ();
public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context)
{
var unit = context.RootNode as SyntaxTree;
if (unit == null)
return Enumerable.Empty<CodeIssue> ();
return new GatherVisitor (context, unit).GetIssues ();
return new GatherVisitor (context).GetIssues ();
}
class GatherVisitor : GatherVisitorBase
{
SyntaxTree unit;
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
public GatherVisitor (BaseRefactoringContext ctx)
: base (ctx)
{
this.unit = unit;
}
static VariableInitializer GetControlVariable(VariableDeclarationStatement variableDecl,
@ -111,25 +102,24 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -111,25 +102,24 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (localResolveResult == null)
return;
var results = ctx.FindReferences (forStatement, localResolveResult.Variable);
var modified = false;
refFinder.FindLocalReferences (localResolveResult.Variable, ctx.UnresolvedFile, unit, ctx.Compilation,
(node, resolveResult) =>
{
if (modified)
return;
var unary = node.Parent as UnaryOperatorExpression;
if (unary != null && unary.Expression == node) {
modified = unary.Operator == UnaryOperatorType.Decrement ||
unary.Operator == UnaryOperatorType.PostDecrement ||
unary.Operator == UnaryOperatorType.Increment ||
unary.Operator == UnaryOperatorType.PostIncrement;
return;
}
var assignment = node.Parent as AssignmentExpression;
modified = assignment != null && assignment.Left == node;
}, ctx.CancellationToken);
foreach (var result in results) {
if (modified)
break;
var node = result.Node;
var unary = node.Parent as UnaryOperatorExpression;
if (unary != null && unary.Expression == node) {
modified = unary.Operator == UnaryOperatorType.Decrement ||
unary.Operator == UnaryOperatorType.PostDecrement ||
unary.Operator == UnaryOperatorType.Increment ||
unary.Operator == UnaryOperatorType.PostIncrement;
continue;
}
var assignment = node.Parent as AssignmentExpression;
modified = assignment != null && assignment.Left == node;
}
if (!modified)
AddIssue (controlVariable.NameToken,

11
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/FormatStringIssues/FormatStringIssue.cs

@ -54,6 +54,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -54,6 +54,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
public override void VisitInvocationExpression(InvocationExpression invocationExpression)
{
base.VisitInvocationExpression(invocationExpression);
// Speed up the inspector by discarding some invocations early
var hasEligibleArgument = invocationExpression.Arguments.Any(argument => {
var primitiveArg = argument as PrimitiveExpression;
return primitiveArg != null && primitiveArg.Value is string;
});
if (!hasEligibleArgument)
return;
var invocationResolveResult = context.Resolve(invocationExpression) as CSharpInvocationResolveResult;
if (invocationResolveResult == null)
return;
@ -65,7 +74,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -65,7 +74,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return;
}
var primitiveArgument = formatArgument as PrimitiveExpression;
if (primitiveArgument == null || !(primitiveArgument.Value is String))
if (primitiveArgument == null || !(primitiveArgument.Value is string))
return;
var format = (string)primitiveArgument.Value;
var parsingResult = context.ParseFormatString(format);

6
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/IncorrectExceptionParameterOrderingIssue.cs

@ -58,9 +58,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -58,9 +58,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
public override void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression)
{
var type = context.Resolve(objectCreateExpression.Type) as TypeResolveResult;
if (type == null)
return;
var parameters = objectCreateExpression.Arguments;
if (parameters.Count != 2)
return;
@ -69,6 +66,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -69,6 +66,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (firstParam == null || firstParam.Value.GetType() != typeof(string) ||
secondParam == null || firstParam.Value.GetType() != typeof(string))
return;
var type = context.Resolve(objectCreateExpression.Type) as TypeResolveResult;
if (type == null)
return;
var leftLength = (firstParam.Value as string).Length;
var rightLength = (secondParam.Value as string).Length;

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

@ -42,11 +42,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -42,11 +42,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context)
{
var unit = context.RootNode as SyntaxTree;
if (unit == null)
return Enumerable.Empty<CodeIssue> ();
return new GatherVisitor (context, unit).GetIssues ();
return new GatherVisitor (context).GetIssues ();
}
class AnalysisStatementCollector : DepthFirstAstVisitor
@ -110,15 +106,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -110,15 +106,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
class GatherVisitor : GatherVisitorBase
{
static FindReferences refFinder = new FindReferences ();
SyntaxTree unit;
HashSet<AstNode> collectedAstNodes;
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
public GatherVisitor (BaseRefactoringContext ctx)
: base (ctx)
{
this.unit = unit;
this.collectedAstNodes = new HashSet<AstNode> ();
}
@ -139,7 +131,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -139,7 +131,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
base.VisitParameterDeclaration (parameterDeclaration);
var resolveResult = ctx.Resolve (parameterDeclaration) as LocalResolveResult;
CollectIssues (parameterDeclaration, resolveResult);
CollectIssues (parameterDeclaration, parameterDeclaration.Parent, resolveResult);
}
public override void VisitVariableInitializer (VariableInitializer variableInitializer)
@ -147,7 +139,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -147,7 +139,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
base.VisitVariableInitializer (variableInitializer);
var resolveResult = ctx.Resolve (variableInitializer) as LocalResolveResult;
CollectIssues (variableInitializer, resolveResult);
CollectIssues (variableInitializer, variableInitializer.Parent.Parent, resolveResult);
}
static bool IsAssignment (AstNode node)
@ -200,42 +192,42 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -200,42 +192,42 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
HashSet<VariableReferenceNode> collectedNodes;
Dictionary<VariableReferenceNode, int> nodeDegree; // number of enumerations a node can reach
void FindReferences (AstNode variableDecl, IVariable variable)
void FindReferences (AstNode variableDecl, AstNode rootNode, IVariable variable)
{
references = new HashSet<AstNode> ();
refStatements = new HashSet<Statement> ();
lambdaExpressions = new HashSet<LambdaExpression> ();
refFinder.FindLocalReferences (variable, ctx.UnresolvedFile, 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, should be analyzed separately
var expr = parent as LambdaExpression;
if (expr != null) {
if (IsAssignment (astNode) || IsEnumeration (astNode)) {
references.Add (astNode);
lambdaExpressions.Add (expr);
}
return;
}
foreach (var result in ctx.FindReferences (rootNode, variable)) {
var astNode = result.Node;
if (astNode == variableDecl)
continue;
var statement = (Statement)parent;
var parent = astNode.Parent;
while (!(parent == null || parent is Statement || parent is LambdaExpression))
parent = parent.Parent;
if (parent == null)
continue;
// lambda expression with expression body, should be analyzed separately
var expr = parent as LambdaExpression;
if (expr != null) {
if (IsAssignment (astNode) || IsEnumeration (astNode)) {
references.Add (astNode);
refStatements.Add (statement);
lambdaExpressions.Add (expr);
}
}, ctx.CancellationToken);
continue;
}
if (IsAssignment (astNode) || IsEnumeration (astNode)) {
references.Add (astNode);
var statement = (Statement)parent;
refStatements.Add (statement);
}
}
}
void CollectIssues (AstNode variableDecl, LocalResolveResult resolveResult)
void CollectIssues (AstNode variableDecl, AstNode rootNode, LocalResolveResult resolveResult)
{
if (resolveResult == null)
return;
@ -246,7 +238,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -246,7 +238,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
typeDef.KnownTypeCode != KnownTypeCode.IEnumerableOfT))
return;
FindReferences (variableDecl, resolveResult.Variable);
FindReferences (variableDecl, rootNode, resolveResult.Variable);
var statements = AnalysisStatementCollector.Collect (variableDecl);
foreach (var statement in statements) {

24
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/OptionalParameterCouldBeSkippedIssue.cs

@ -66,15 +66,24 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -66,15 +66,24 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
(invocation, args) => new InvocationExpression(invocation.Target.Clone(), args));
}
void CheckMethodCall<T> (T node, IEnumerable<Expression> arguments, Func<T, IEnumerable<Expression>, T> generateReplacement) where T: AstNode
void CheckMethodCall<T> (T node, IEnumerable<Expression> args, Func<T, IEnumerable<Expression>, T> generateReplacement) where T: AstNode
{
// The first two checks are unnecessary, but eliminates the majority of calls early,
// improving performance.
var arguments = args.ToArray();
if (arguments.Length == 0)
return;
var lastArg = arguments[arguments.Length - 1];
if (!(lastArg is PrimitiveExpression || lastArg is NamedArgumentExpression))
return;
var invocationResolveResult = ctx.Resolve(node) as CSharpInvocationResolveResult;
if (invocationResolveResult == null)
return;
string actionMessage = ctx.TranslateString("Remove redundant arguments");
var redundantArguments = GetRedundantArguments(arguments.ToArray(), invocationResolveResult);
var redundantArguments = GetRedundantArguments(arguments, invocationResolveResult);
var action = new CodeAction(actionMessage, script => {
var newArgumentList = arguments
.Where(arg => !redundantArguments.Contains(arg))
@ -84,7 +93,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -84,7 +93,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
});
var issueMessage = ctx.TranslateString("Argument is identical to the default value");
var lastPositionalArgument = redundantArguments.FirstOrDefault(expression => !(expression is NamedArgumentExpression));
bool hasNamedArguments = false;
foreach (var argument in redundantArguments) {
var localArgument = argument;
@ -100,7 +108,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -100,7 +108,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
var newInvocation = generateReplacement(node, newArgumentList);
script.Replace(node, newInvocation);
}));
hasNamedArguments = true;
} else {
var title = ctx.TranslateString("Remove this and the following positional arguments");
actions.Add(new CodeAction(title, script => {
@ -116,11 +123,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -116,11 +123,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
}
}
IEnumerable<Expression> GetRedundantArguments(Expression[] arguments, CSharpInvocationResolveResult invocationResolveResult)
IList<Expression> GetRedundantArguments(Expression[] arguments, CSharpInvocationResolveResult invocationResolveResult)
{
var argumentToParameterMap = invocationResolveResult.GetArgumentToParameterMap();
var resolvedParameters = invocationResolveResult.Member.Parameters;
IList<Expression> redundantArguments = new List<Expression>();
for (int i = arguments.Length - 1; i >= 0; i--) {
var parameterIndex = argumentToParameterMap[i];
if (parameterIndex == -1)
@ -141,7 +150,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -141,7 +150,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
// Stop here since any arguments before this one has to be there
// to enable the passing of this argument
break;
yield return argument;
redundantArguments.Add(argument);
} else if (argument is NamedArgumentExpression) {
var expression = ((NamedArgumentExpression)argument).Expression as PrimitiveExpression;
if (expression == null)
@ -150,12 +159,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -150,12 +159,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (expressionResolveResult == null || parameter.ConstantValue != expressionResolveResult.ConstantValue)
// continue, since there can still be more arguments that are redundant
continue;
yield return argument;
redundantArguments.Add(argument);
} else {
// This is a non-constant positional argument => no more redundancies are possible
break;
}
}
return redundantArguments;
}
}
}

57
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantAssignmentIssue.cs

@ -26,7 +26,6 @@ @@ -26,7 +26,6 @@
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.Semantics;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
@ -48,14 +47,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -48,14 +47,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
class GatherVisitor : GatherVisitorBase
{
static FindReferences refFinder = new FindReferences ();
SyntaxTree unit;
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
: base (ctx)
{
this.unit = unit;
}
public override void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration)
@ -89,34 +83,39 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -89,34 +83,39 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
if (rootStatement == null || resolveResult == null)
return;
var references = new HashSet<AstNode> ();
var refStatements = new HashSet<Statement> ();
var usedInLambda = false;
refFinder.FindLocalReferences (resolveResult.Variable, ctx.UnresolvedFile, unit, ctx.Compilation,
(astNode, rr) => {
if (usedInLambda || astNode == variableDecl)
return;
var parent = astNode.Parent;
while (!(parent == null || parent is Statement || parent is LambdaExpression))
parent = parent.Parent;
if (parent == null)
return;
var statement = parent as Statement;
if (statement != null) {
references.Add (astNode);
refStatements.Add (statement);
}
var results = ctx.FindReferences (rootStatement, resolveResult.Variable);
foreach (var result in results) {
var node = result.Node;
if (node == variableDecl)
continue;
var parent = node.Parent;
while (!(parent == null || parent is Statement || parent is LambdaExpression))
parent = parent.Parent;
if (parent == null)
continue;
var statement = parent as Statement;
if (statement != null) {
references.Add (node);
refStatements.Add (statement);
}
while (parent != null && parent != rootStatement) {
if (parent is LambdaExpression || parent is AnonymousMethodExpression) {
usedInLambda = true;
break;
}
parent = parent.Parent;
while (parent != null && parent != rootStatement) {
if (parent is LambdaExpression || parent is AnonymousMethodExpression) {
usedInLambda = true;
break;
}
}, ctx.CancellationToken);
parent = parent.Parent;
}
if (usedInLambda) {
break;
}
}
// stop analyzing if the variable is used in any lambda expression or anonymous method
if (usedInLambda)

17
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantToStringIssue.cs

@ -64,7 +64,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -64,7 +64,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
void CheckExpressionInAutoCallContext(Expression expression)
{
if (expression is InvocationExpression) {
if (expression is InvocationExpression && !processedNodes.Contains(expression)) {
CheckInvocationInAutoCallContext((InvocationExpression)expression);
}
}
@ -75,12 +75,12 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -75,12 +75,12 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (memberExpression == null) {
return;
}
var resolveResult = ctx.Resolve(invocationExpression) as CSharpInvocationResolveResult;
if (resolveResult == null) {
if (memberExpression.MemberName != "ToString" || invocationExpression.Arguments.Any ()) {
return;
}
var member = resolveResult.Member;
if (member.Name != "ToString" || member.Parameters.Count != 0) {
var resolveResult = ctx.Resolve(invocationExpression) as CSharpInvocationResolveResult;
if (resolveResult == null) {
return;
}
AddRedundantToStringIssue(memberExpression, invocationExpression);
@ -88,9 +88,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -88,9 +88,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
void AddRedundantToStringIssue(MemberReferenceExpression memberExpression, InvocationExpression invocationExpression)
{
if (processedNodes.Contains(invocationExpression)) {
return;
}
// Simon Lindgren 2012-09-14: Previously there was a check here to see if the node had already been processed
// This has been moved out to the callers, to check it earlier for a 30-40% run time reduction
processedNodes.Add(invocationExpression);
AddIssue(memberExpression.DotToken.StartLocation, invocationExpression.RParToken.EndLocation,
@ -176,7 +175,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -176,7 +175,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
void CheckTargetedObject(InvocationExpression invocationExpression, IType type, IMember member)
{
var memberExpression = invocationExpression.Target as MemberReferenceExpression;
if (memberExpression != null) {
if (memberExpression != null && !processedNodes.Contains(invocationExpression)) {
if (type.IsKnownType(KnownTypeCode.String) && member.Name == "ToString") {
AddRedundantToStringIssue(memberExpression, invocationExpression);
}

5
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ReferenceEqualsCalledWithValueTypeIssue.cs

@ -54,6 +54,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -54,6 +54,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
base.VisitInvocationExpression (invocationExpression);
// Quickly determine if this invocation is eligible to speed up the inspector
var nameToken = invocationExpression.Target.GetChildByRole(Roles.Identifier);
if (nameToken.Name != "ReferenceEquals")
return;
var resolveResult = ctx.Resolve (invocationExpression) as InvocationResolveResult;
if (resolveResult == null ||
resolveResult.Member.DeclaringTypeDefinition == null ||

19
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ValueParameterUnusedIssue.cs

@ -45,13 +45,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -45,13 +45,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
class GatherVisitor : GatherVisitorBase
{
readonly BaseRefactoringContext context;
readonly FindReferences findRef = new FindReferences();
public GatherVisitor(BaseRefactoringContext context, ValueParameterUnusedIssue inspector) : base (context)
{
this.context = context;
}
public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration)
@ -77,22 +72,22 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -77,22 +72,22 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (!IsEligible(body))
return;
var localResolveResult = context.GetResolverStateBefore(body)
var localResolveResult = ctx.GetResolverStateBefore(body)
.LookupSimpleNameOrTypeName("value", new List<IType>(), NameLookupMode.Expression) as LocalResolveResult;
if (localResolveResult == null)
return;
var variable = localResolveResult.Variable;
bool referenceFound = false;
var syntaxTree = (SyntaxTree)context.RootNode;
findRef.FindLocalReferences(variable, context.UnresolvedFile, syntaxTree, context.Compilation, (n, entity) => {
if (n.StartLocation >= body.StartLocation && n.EndLocation <= body.EndLocation) {
foreach (var result in ctx.FindReferences (body, localResolveResult.Variable)) {
var node = result.Node;
if (node.StartLocation >= body.StartLocation && node.EndLocation <= body.EndLocation) {
referenceFound = true;
break;
}
}, CancellationToken.None);
}
if(!referenceFound)
AddIssue(anchor, context.TranslateString("The " + accessorName + " does not use the 'value' parameter"));
AddIssue(anchor, ctx.TranslateString("The " + accessorName + " does not use the 'value' parameter"));
}
static bool IsEligible(BlockStatement body)

26
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/LocalVariableNotUsedIssue.cs

@ -25,6 +25,8 @@ @@ -25,6 +25,8 @@
// THE SOFTWARE.
using ICSharpCode.NRefactory.Semantics;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
@ -33,21 +35,22 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -33,21 +35,22 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
Category = IssueCategories.Redundancies,
Severity = Severity.Warning,
IssueMarker = IssueMarker.GrayOut)]
public class LocalVariableNotUsedIssue : VariableNotUsedIssue
public class LocalVariableNotUsedIssue : ICodeIssueProvider
{
internal override GatherVisitorBase GetGatherVisitor (BaseRefactoringContext context, SyntaxTree unit)
#region ICodeIssueProvider implementation
public System.Collections.Generic.IEnumerable<CodeIssue> GetIssues(BaseRefactoringContext context)
{
return new GatherVisitor (context, unit);
return new GatherVisitor (context).GetIssues ();
}
#endregion
class GatherVisitor : GatherVisitorBase
{
SyntaxTree unit;
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
public GatherVisitor (BaseRefactoringContext ctx)
: base (ctx)
{
this.unit = unit;
}
public override void VisitVariableInitializer (VariableInitializer variableInitializer)
@ -65,7 +68,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -65,7 +68,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (resolveResult == null)
return;
if (FindUsage (ctx, unit, resolveResult.Variable, variableInitializer))
if (IsUsed (decl.Parent, resolveResult.Variable, variableInitializer))
return;
AddIssue (variableInitializer.NameToken, ctx.TranslateString ("Remove unused local variable"),
@ -90,11 +93,16 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -90,11 +93,16 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (resolveResult == null)
return;
if (FindUsage (ctx, unit, resolveResult.Variable, foreachStatement.VariableNameToken))
if (IsUsed (foreachStatement, resolveResult.Variable, foreachStatement.VariableNameToken))
return;
AddIssue (foreachStatement.VariableNameToken, ctx.TranslateString ("Local variable is never used"));
}
bool IsUsed(AstNode rootNode, IVariable variable, AstNode variableNode)
{
return ctx.FindReferences(rootNode, variable).Any(result => result.Node != variableNode);
}
}
}

18
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/ParameterNotUsedIssue.cs

@ -27,6 +27,7 @@ @@ -27,6 +27,7 @@
using ICSharpCode.NRefactory.Semantics;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
using System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
@ -35,22 +36,20 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -35,22 +36,20 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
Category = IssueCategories.Redundancies,
Severity = Severity.Warning,
IssueMarker = IssueMarker.GrayOut)]
public class ParameterNotUsedIssue : VariableNotUsedIssue
public class ParameterNotUsedIssue : ICodeIssueProvider
{
internal override GatherVisitorBase GetGatherVisitor (BaseRefactoringContext context, SyntaxTree unit)
#region ICodeIssueProvider implementation
public IEnumerable<CodeIssue> GetIssues(BaseRefactoringContext context)
{
return new GatherVisitor (context, unit);
return new GatherVisitor (context).GetIssues ();
}
#endregion
class GatherVisitor : GatherVisitorBase
{
SyntaxTree unit;
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
public GatherVisitor (BaseRefactoringContext ctx)
: base (ctx)
{
this.unit = unit;
}
public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration)
@ -81,7 +80,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -81,7 +80,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
var resolveResult = ctx.Resolve (parameterDeclaration) as LocalResolveResult;
if (resolveResult == null)
return;
if (FindUsage (ctx, unit, resolveResult.Variable, parameterDeclaration))
if (ctx.FindReferences (parameterDeclaration.Parent, resolveResult.Variable).Any(r => r.Node != parameterDeclaration))
return;
AddIssue (parameterDeclaration.NameToken, ctx.TranslateString ("Parameter is never used"));

60
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/VariableNotUsedIssue.cs

@ -1,60 +0,0 @@ @@ -1,60 +0,0 @@
//
// VariableNotUsedIssue.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.Resolver;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public abstract class VariableNotUsedIssue : ICodeIssueProvider
{
static FindReferences refFinder = new FindReferences ();
public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context)
{
var unit = context.RootNode as SyntaxTree;
if (unit == null)
return Enumerable.Empty<CodeIssue> ();
return GetGatherVisitor(context, unit).GetIssues ();
}
protected static bool FindUsage (BaseRefactoringContext context, SyntaxTree unit, IVariable variable,
AstNode declaration)
{
var found = false;
refFinder.FindLocalReferences (variable, context.UnresolvedFile, unit, context.Compilation,
(node, resolveResult) =>
{
found = found || node != declaration;
}, context.CancellationToken);
return found;
}
internal abstract GatherVisitorBase GetGatherVisitor (BaseRefactoringContext context, SyntaxTree unit);
}
}

11
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableOnlyAssignedIssues/LocalVariableOnlyAssignedIssue.cs

@ -36,19 +36,16 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -36,19 +36,16 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
IssueMarker = IssueMarker.Underline)]
public class LocalVariableOnlyAssignedIssue : VariableOnlyAssignedIssue
{
internal override GatherVisitorBase GetGatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
internal override GatherVisitorBase GetGatherVisitor (BaseRefactoringContext ctx)
{
return new GatherVisitor (ctx, unit);
return new GatherVisitor (ctx);
}
class GatherVisitor : GatherVisitorBase
{
SyntaxTree unit;
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
public GatherVisitor (BaseRefactoringContext ctx)
: base (ctx)
{
this.unit = unit;
}
public override void VisitVariableInitializer (VariableInitializer variableInitializer)
@ -62,7 +59,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -62,7 +59,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
var resolveResult = ctx.Resolve (variableInitializer) as LocalResolveResult;
if (resolveResult == null)
return;
if (!TestOnlyAssigned (ctx, unit, resolveResult.Variable))
if (!TestOnlyAssigned (ctx, decl.Parent, resolveResult.Variable))
return;
AddIssue (variableInitializer.NameToken,
ctx.TranslateString ("Local variable is assigned by its value is never used"));

27
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableOnlyAssignedIssues/ParameterOnlyAssignedIssue.cs

@ -23,7 +23,6 @@ @@ -23,7 +23,6 @@
// 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.Semantics;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
@ -35,33 +34,33 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -35,33 +34,33 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
IssueMarker = IssueMarker.Underline)]
public class ParameterOnlyAssignedIssue : VariableOnlyAssignedIssue
{
internal override GatherVisitorBase GetGatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
internal override GatherVisitorBase GetGatherVisitor(BaseRefactoringContext ctx)
{
return new GatherVisitor (ctx, unit);
return new GatherVisitor(ctx);
}
private class GatherVisitor : GatherVisitorBase
{
SyntaxTree unit;
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
public GatherVisitor(BaseRefactoringContext ctx)
: base (ctx)
{
this.unit = unit;
}
public override void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration)
public override void VisitParameterDeclaration(ParameterDeclaration parameterDeclaration)
{
base.VisitParameterDeclaration (parameterDeclaration);
base.VisitParameterDeclaration(parameterDeclaration);
var resolveResult = ctx.Resolve (parameterDeclaration) as LocalResolveResult;
var resolveResult = ctx.Resolve(parameterDeclaration) as LocalResolveResult;
if (resolveResult == null)
return;
if (parameterDeclaration.ParameterModifier == ParameterModifier.Out || parameterDeclaration.ParameterModifier == ParameterModifier.Ref
|| !TestOnlyAssigned (ctx, unit, resolveResult.Variable))
var parameterModifier = parameterDeclaration.ParameterModifier;
if (parameterModifier == ParameterModifier.Out || parameterModifier == ParameterModifier.Ref ||
!TestOnlyAssigned(ctx, parameterDeclaration.Parent, resolveResult.Variable)) {
return;
AddIssue (parameterDeclaration.NameToken,
ctx.TranslateString ("Parameter is assigned by its value is never used"));
}
AddIssue(parameterDeclaration.NameToken,
ctx.TranslateString("Parameter is assigned by its value is never used"));
}
}
}

69
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableOnlyAssignedIssues/VariableOnlyAssignedIssue.cs

@ -25,68 +25,61 @@ @@ -25,68 +25,61 @@
// THE SOFTWARE.
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public abstract class VariableOnlyAssignedIssue : ICodeIssueProvider
{
static FindReferences refFinder = new FindReferences ();
public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context)
{
var unit = context.RootNode as SyntaxTree;
if (unit == null)
return Enumerable.Empty<CodeIssue> ();
return GetGatherVisitor (context, unit).GetIssues ();
return GetGatherVisitor (context).GetIssues ();
}
protected static bool TestOnlyAssigned (BaseRefactoringContext ctx, SyntaxTree unit, IVariable variable)
protected static bool TestOnlyAssigned(BaseRefactoringContext ctx, AstNode rootNode, IVariable variable)
{
var assignment = false;
var nonAssignment = false;
refFinder.FindLocalReferences (variable, ctx.UnresolvedFile, unit, ctx.Compilation,
(node, resolveResult) =>
{
if (node is ParameterDeclaration)
return;
foreach (var result in ctx.FindReferences(rootNode, variable)) {
var node = result.Node;
if (node is ParameterDeclaration)
continue;
if (node is VariableInitializer) {
if (!(node as VariableInitializer).Initializer.IsNull)
assignment = true;
return;
}
if (node is VariableInitializer) {
if (!(node as VariableInitializer).Initializer.IsNull)
assignment = true;
continue;
}
if (node is IdentifierExpression) {
var parent = node.Parent;
if (parent is AssignmentExpression) {
if (((AssignmentExpression)parent).Left == node) {
assignment = true;
return;
}
} else if (parent is UnaryOperatorExpression) {
var op = ((UnaryOperatorExpression)parent).Operator;
switch (op) {
if (node is IdentifierExpression) {
var parent = node.Parent;
if (parent is AssignmentExpression) {
if (((AssignmentExpression)parent).Left == node) {
assignment = true;
continue;
}
} else if (parent is UnaryOperatorExpression) {
var op = ((UnaryOperatorExpression)parent).Operator;
switch (op) {
case UnaryOperatorType.Increment:
case UnaryOperatorType.PostIncrement:
case UnaryOperatorType.Decrement:
case UnaryOperatorType.PostDecrement:
assignment = true;
return;
}
} else if (parent is DirectionExpression) {
if (((DirectionExpression)parent).FieldDirection == FieldDirection.Out) {
assignment = true;
return;
}
continue;
}
} else if (parent is DirectionExpression) {
if (((DirectionExpression)parent).FieldDirection == FieldDirection.Out) {
assignment = true;
continue;
}
}
nonAssignment = true;
}, ctx.CancellationToken);
}
nonAssignment = true;
}
return assignment && !nonAssignment;
}
internal abstract GatherVisitorBase GetGatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit);
internal abstract GatherVisitorBase GetGatherVisitor (BaseRefactoringContext ctx);
}
}

157
ICSharpCode.NRefactory.CSharp/Refactoring/LocalReferenceFinder.cs

@ -0,0 +1,157 @@ @@ -0,0 +1,157 @@
//
// LocalReferenceFinder.cs
//
// Author:
// Simon Lindgren <simon.n.lindgren@gmail.com>
//
// Copyright (c) 2012 Simon Lindgren
//
// 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.Resolver;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Utils;
using System.Diagnostics;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// Finds references to <see cref="IVariable">IVariables</see>.
/// </summary>
/// <remarks>
/// This class is more efficient than <see cref="FindReferences"/>
/// if there is already a resolved tree or if multiple searches needs
/// to be performed.
/// </remarks>
public class LocalReferenceFinder
{
LocalReferenceLocator locator;
MultiDictionary<IVariable, ReferenceResult> references = new MultiDictionary<IVariable, ReferenceResult>();
HashSet<AstNode> visitedRoots = new HashSet<AstNode>();
public LocalReferenceFinder(CSharpAstResolver resolver)
{
locator = new LocalReferenceLocator(resolver, this);
}
public LocalReferenceFinder(BaseRefactoringContext context) : this(context.Resolver)
{
}
void VisitIfNeccessary(AstNode rootNode)
{
// If any of the parent nodes are recorded as visited,
// we don't need to traverse rootNode this time.
var tmpRoot = rootNode;
while(tmpRoot != null){
if (visitedRoots.Contains(tmpRoot))
return;
tmpRoot = tmpRoot.Parent;
}
locator.ProccessRoot (rootNode);
}
/// <summary>
/// Finds the references to <paramref name="variable"/>.
/// </summary>
/// <param name='rootNode'>
/// Root node for the search.
/// </param>
/// <param name='variable'>
/// The variable to find references for.
/// </param>
/// <param name='action'>
/// Will be called for each reference found.
/// </param>
/// <remarks>
/// When a single <see cref="LocalReferenceFinder"/> is reused for multiple
/// searches, which references outside of <paramref name="rootNode"/> are
/// or are not reported is undefined.
/// </remarks>
public IList<ReferenceResult> FindReferences(AstNode rootNode, IVariable variable)
{
lock (locator) {
VisitIfNeccessary(rootNode);
var lookup = (ILookup<IVariable, ReferenceResult>)references;
if (!lookup.Contains(variable))
return new List<ReferenceResult>();
// Clone the list for thread safety
return references[variable].ToList();
}
}
class LocalReferenceLocator : DepthFirstAstVisitor
{
CSharpAstResolver resolver;
LocalReferenceFinder referenceFinder;
public LocalReferenceLocator(CSharpAstResolver resolver, LocalReferenceFinder referenceFinder)
{
this.resolver = resolver;
this.referenceFinder = referenceFinder;
}
IList<IVariable> processedVariables = new List<IVariable>();
public void ProccessRoot (AstNode rootNode)
{
rootNode.AcceptVisitor(this);
referenceFinder.visitedRoots.Add(rootNode);
}
protected override void VisitChildren(AstNode node)
{
if (referenceFinder.visitedRoots.Contains(node))
return;
var localResolveResult = resolver.Resolve(node) as LocalResolveResult;
if (localResolveResult != null && !processedVariables.Contains(localResolveResult.Variable)) {
referenceFinder.references.Add(localResolveResult.Variable, new ReferenceResult(node, localResolveResult));
processedVariables.Add(localResolveResult.Variable);
base.VisitChildren(node);
Debug.Assert(processedVariables.Contains(localResolveResult.Variable), "Variable should still be in the list of processed variables.");
processedVariables.Remove(localResolveResult.Variable);
} else {
base.VisitChildren(node);
}
}
}
}
public class ReferenceResult
{
public ReferenceResult (AstNode node, LocalResolveResult resolveResult)
{
Node = node;
ResolveResult = resolveResult;
}
public AstNode Node { get; private set; }
public LocalResolveResult ResolveResult { get; private set; }
}
}
Loading…
Cancel
Save