Browse Source

Merge pull request #116 from riviti/performance

Performance enhancements
Mike Krüger 13 years ago
parent
commit
063a72c685
  1. 4
      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

4
ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj

@ -324,7 +324,6 @@
<Compile Include="Refactoring\CodeIssues\VariableNotUsedIssues\LocalVariableNotUsedIssue.cs" /> <Compile Include="Refactoring\CodeIssues\VariableNotUsedIssues\LocalVariableNotUsedIssue.cs" />
<Compile Include="Refactoring\CodeIssues\VariableNotUsedIssues\ParameterNotUsedIssue.cs" /> <Compile Include="Refactoring\CodeIssues\VariableNotUsedIssues\ParameterNotUsedIssue.cs" />
<Compile Include="Refactoring\CodeIssues\TypeParameterNotUsedIssue.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\ParameterOnlyAssignedIssue.cs" />
<Compile Include="Refactoring\CodeIssues\VariableOnlyAssignedIssues\VariableOnlyAssignedIssue.cs" /> <Compile Include="Refactoring\CodeIssues\VariableOnlyAssignedIssues\VariableOnlyAssignedIssue.cs" />
<Compile Include="Refactoring\DocumentScript.cs" /> <Compile Include="Refactoring\DocumentScript.cs" />
@ -503,6 +502,7 @@
<Compile Include="Refactoring\CodeIssues\ParameterCanBeDemotedIssue\SupportsIndexingCriterion.cs" /> <Compile Include="Refactoring\CodeIssues\ParameterCanBeDemotedIssue\SupportsIndexingCriterion.cs" />
<Compile Include="Refactoring\CodeIssues\RedundantWhereWithPredicateIssue.cs" /> <Compile Include="Refactoring\CodeIssues\RedundantWhereWithPredicateIssue.cs" />
<Compile Include="Refactoring\CodeActions\ConvertToInitializer\AccessPath.cs" /> <Compile Include="Refactoring\CodeActions\ConvertToInitializer\AccessPath.cs" />
<Compile Include="Refactoring\LocalReferenceFinder.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> <ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
@ -534,4 +534,4 @@
<ItemGroup> <ItemGroup>
<None Include="Parser\mcs\cs-parser.jay" /> <None Include="Parser\mcs\cs-parser.jay" />
</ItemGroup> </ItemGroup>
</Project> </Project>

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

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

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

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

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

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

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

@ -54,6 +54,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
public override void VisitInvocationExpression(InvocationExpression invocationExpression) public override void VisitInvocationExpression(InvocationExpression invocationExpression)
{ {
base.VisitInvocationExpression(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; var invocationResolveResult = context.Resolve(invocationExpression) as CSharpInvocationResolveResult;
if (invocationResolveResult == null) if (invocationResolveResult == null)
return; return;
@ -65,7 +74,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return; return;
} }
var primitiveArgument = formatArgument as PrimitiveExpression; var primitiveArgument = formatArgument as PrimitiveExpression;
if (primitiveArgument == null || !(primitiveArgument.Value is String)) if (primitiveArgument == null || !(primitiveArgument.Value is string))
return; return;
var format = (string)primitiveArgument.Value; var format = (string)primitiveArgument.Value;
var parsingResult = context.ParseFormatString(format); var parsingResult = context.ParseFormatString(format);

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

@ -58,9 +58,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
public override void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) public override void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression)
{ {
var type = context.Resolve(objectCreateExpression.Type) as TypeResolveResult;
if (type == null)
return;
var parameters = objectCreateExpression.Arguments; var parameters = objectCreateExpression.Arguments;
if (parameters.Count != 2) if (parameters.Count != 2)
return; return;
@ -69,6 +66,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (firstParam == null || firstParam.Value.GetType() != typeof(string) || if (firstParam == null || firstParam.Value.GetType() != typeof(string) ||
secondParam == null || firstParam.Value.GetType() != typeof(string)) secondParam == null || firstParam.Value.GetType() != typeof(string))
return; return;
var type = context.Resolve(objectCreateExpression.Type) as TypeResolveResult;
if (type == null)
return;
var leftLength = (firstParam.Value as string).Length; var leftLength = (firstParam.Value as string).Length;
var rightLength = (secondParam.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
{ {
public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context) public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context)
{ {
var unit = context.RootNode as SyntaxTree; return new GatherVisitor (context).GetIssues ();
if (unit == null)
return Enumerable.Empty<CodeIssue> ();
return new GatherVisitor (context, unit).GetIssues ();
} }
class AnalysisStatementCollector : DepthFirstAstVisitor class AnalysisStatementCollector : DepthFirstAstVisitor
@ -110,15 +106,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
class GatherVisitor : GatherVisitorBase class GatherVisitor : GatherVisitorBase
{ {
static FindReferences refFinder = new FindReferences ();
SyntaxTree unit;
HashSet<AstNode> collectedAstNodes; HashSet<AstNode> collectedAstNodes;
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit) public GatherVisitor (BaseRefactoringContext ctx)
: base (ctx) : base (ctx)
{ {
this.unit = unit;
this.collectedAstNodes = new HashSet<AstNode> (); this.collectedAstNodes = new HashSet<AstNode> ();
} }
@ -139,7 +131,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
base.VisitParameterDeclaration (parameterDeclaration); base.VisitParameterDeclaration (parameterDeclaration);
var resolveResult = ctx.Resolve (parameterDeclaration) as LocalResolveResult; var resolveResult = ctx.Resolve (parameterDeclaration) as LocalResolveResult;
CollectIssues (parameterDeclaration, resolveResult); CollectIssues (parameterDeclaration, parameterDeclaration.Parent, resolveResult);
} }
public override void VisitVariableInitializer (VariableInitializer variableInitializer) public override void VisitVariableInitializer (VariableInitializer variableInitializer)
@ -147,7 +139,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
base.VisitVariableInitializer (variableInitializer); base.VisitVariableInitializer (variableInitializer);
var resolveResult = ctx.Resolve (variableInitializer) as LocalResolveResult; var resolveResult = ctx.Resolve (variableInitializer) as LocalResolveResult;
CollectIssues (variableInitializer, resolveResult); CollectIssues (variableInitializer, variableInitializer.Parent.Parent, resolveResult);
} }
static bool IsAssignment (AstNode node) static bool IsAssignment (AstNode node)
@ -200,42 +192,42 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
HashSet<VariableReferenceNode> collectedNodes; HashSet<VariableReferenceNode> collectedNodes;
Dictionary<VariableReferenceNode, int> nodeDegree; // number of enumerations a node can reach 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> (); references = new HashSet<AstNode> ();
refStatements = new HashSet<Statement> (); refStatements = new HashSet<Statement> ();
lambdaExpressions = new HashSet<LambdaExpression> (); lambdaExpressions = new HashSet<LambdaExpression> ();
refFinder.FindLocalReferences (variable, ctx.UnresolvedFile, unit, ctx.Compilation, foreach (var result in ctx.FindReferences (rootNode, variable)) {
(astNode, resolveResult) => { var astNode = result.Node;
if (astNode == variableDecl) if (astNode == variableDecl)
return; continue;
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;
}
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)) { if (IsAssignment (astNode) || IsEnumeration (astNode)) {
references.Add (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) if (resolveResult == null)
return; return;
@ -246,7 +238,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
typeDef.KnownTypeCode != KnownTypeCode.IEnumerableOfT)) typeDef.KnownTypeCode != KnownTypeCode.IEnumerableOfT))
return; return;
FindReferences (variableDecl, resolveResult.Variable); FindReferences (variableDecl, rootNode, resolveResult.Variable);
var statements = AnalysisStatementCollector.Collect (variableDecl); var statements = AnalysisStatementCollector.Collect (variableDecl);
foreach (var statement in statements) { foreach (var statement in statements) {

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

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

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

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

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

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

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

@ -54,6 +54,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{ {
base.VisitInvocationExpression (invocationExpression); 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; var resolveResult = ctx.Resolve (invocationExpression) as InvocationResolveResult;
if (resolveResult == null || if (resolveResult == null ||
resolveResult.Member.DeclaringTypeDefinition == null || resolveResult.Member.DeclaringTypeDefinition == null ||

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

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

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

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

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

@ -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
IssueMarker = IssueMarker.Underline)] IssueMarker = IssueMarker.Underline)]
public class LocalVariableOnlyAssignedIssue : VariableOnlyAssignedIssue 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 class GatherVisitor : GatherVisitorBase
{ {
SyntaxTree unit; public GatherVisitor (BaseRefactoringContext ctx)
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
: base (ctx) : base (ctx)
{ {
this.unit = unit;
} }
public override void VisitVariableInitializer (VariableInitializer variableInitializer) public override void VisitVariableInitializer (VariableInitializer variableInitializer)
@ -62,7 +59,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
var resolveResult = ctx.Resolve (variableInitializer) as LocalResolveResult; var resolveResult = ctx.Resolve (variableInitializer) as LocalResolveResult;
if (resolveResult == null) if (resolveResult == null)
return; return;
if (!TestOnlyAssigned (ctx, unit, resolveResult.Variable)) if (!TestOnlyAssigned (ctx, decl.Parent, resolveResult.Variable))
return; return;
AddIssue (variableInitializer.NameToken, AddIssue (variableInitializer.NameToken,
ctx.TranslateString ("Local variable is assigned by its value is never used")); 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 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // 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 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.Semantics;
namespace ICSharpCode.NRefactory.CSharp.Refactoring namespace ICSharpCode.NRefactory.CSharp.Refactoring
@ -35,33 +34,33 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
IssueMarker = IssueMarker.Underline)] IssueMarker = IssueMarker.Underline)]
public class ParameterOnlyAssignedIssue : VariableOnlyAssignedIssue 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 private class GatherVisitor : GatherVisitorBase
{ {
SyntaxTree unit; public GatherVisitor(BaseRefactoringContext ctx)
public GatherVisitor (BaseRefactoringContext ctx, SyntaxTree unit)
: base (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) if (resolveResult == null)
return; 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; 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 @@
// THE SOFTWARE. // THE SOFTWARE.
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring namespace ICSharpCode.NRefactory.CSharp.Refactoring
{ {
public abstract class VariableOnlyAssignedIssue : ICodeIssueProvider public abstract class VariableOnlyAssignedIssue : ICodeIssueProvider
{ {
static FindReferences refFinder = new FindReferences ();
public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context) public IEnumerable<CodeIssue> GetIssues (BaseRefactoringContext context)
{ {
var unit = context.RootNode as SyntaxTree; return GetGatherVisitor (context).GetIssues ();
if (unit == null)
return Enumerable.Empty<CodeIssue> ();
return GetGatherVisitor (context, unit).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 assignment = false;
var nonAssignment = false; var nonAssignment = false;
refFinder.FindLocalReferences (variable, ctx.UnresolvedFile, unit, ctx.Compilation, foreach (var result in ctx.FindReferences(rootNode, variable)) {
(node, resolveResult) => var node = result.Node;
{ if (node is ParameterDeclaration)
if (node is ParameterDeclaration) continue;
return;
if (node is VariableInitializer) { if (node is VariableInitializer) {
if (!(node as VariableInitializer).Initializer.IsNull) if (!(node as VariableInitializer).Initializer.IsNull)
assignment = true; assignment = true;
return; continue;
} }
if (node is IdentifierExpression) { if (node is IdentifierExpression) {
var parent = node.Parent; var parent = node.Parent;
if (parent is AssignmentExpression) { if (parent is AssignmentExpression) {
if (((AssignmentExpression)parent).Left == node) { if (((AssignmentExpression)parent).Left == node) {
assignment = true; assignment = true;
return; continue;
} }
} else if (parent is UnaryOperatorExpression) { } else if (parent is UnaryOperatorExpression) {
var op = ((UnaryOperatorExpression)parent).Operator; var op = ((UnaryOperatorExpression)parent).Operator;
switch (op) { switch (op) {
case UnaryOperatorType.Increment: case UnaryOperatorType.Increment:
case UnaryOperatorType.PostIncrement: case UnaryOperatorType.PostIncrement:
case UnaryOperatorType.Decrement: case UnaryOperatorType.Decrement:
case UnaryOperatorType.PostDecrement: case UnaryOperatorType.PostDecrement:
assignment = true; assignment = true;
return; continue;
} }
} else if (parent is DirectionExpression) { } else if (parent is DirectionExpression) {
if (((DirectionExpression)parent).FieldDirection == FieldDirection.Out) { if (((DirectionExpression)parent).FieldDirection == FieldDirection.Out) {
assignment = true; assignment = true;
return; continue;
}
} }
} }
nonAssignment = true; }
}, ctx.CancellationToken); nonAssignment = true;
}
return assignment && !nonAssignment; 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 @@
//
// 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