diff --git a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
index 4032c95608..0a8f9376f1 100644
--- a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
+++ b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
@@ -431,6 +431,8 @@
+
+
diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/HasMemberCriterion.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/HasMemberCriterion.cs
index efcdb14aaf..36aa1d39f5 100644
--- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/HasMemberCriterion.cs
+++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/HasMemberCriterion.cs
@@ -61,17 +61,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
var implementedInterfaceMembers = member.MemberDefinition.ImplementedInterfaceMembers;
if (implementedInterfaceMembers.Any()) {
- return acceptableMembers.ContainsAny(implementedInterfaceMembers);
+ return ContainsAny(acceptableMembers, implementedInterfaceMembers);
}
else {
return acceptableMembers.Contains(member.MemberDefinition);
}
}
- }
-
- static class IEnumerableExtensions
- {
- public static bool ContainsAny(this IEnumerable collection, IEnumerable items)
+
+ static bool ContainsAny(IEnumerable collection, IEnumerable items)
{
foreach (var item in items) {
if (collection.Contains(item))
diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/IsArrayTypeCriterion.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/IsArrayTypeCriterion.cs
new file mode 100644
index 0000000000..db011b3560
--- /dev/null
+++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/IsArrayTypeCriterion.cs
@@ -0,0 +1,43 @@
+//
+// IsArrayTypeCriterion.cs
+//
+// Author:
+// Simon Lindgren
+//
+// 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;
+using ICSharpCode.NRefactory.TypeSystem;
+
+namespace ICSharpCode.NRefactory.CSharp.Refactoring
+{
+ public class IsArrayTypeCriterion : ITypeCriterion
+ {
+ #region ITypeCriterion implementation
+
+ public bool SatisfiedBy(IType type)
+ {
+ return type is ArrayType;
+ }
+
+ #endregion
+ }
+}
+
diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs
index b64a87c446..367e76b8e9 100644
--- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs
+++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs
@@ -28,6 +28,9 @@ using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using System.Linq;
using System;
+using ICSharpCode.NRefactory.CSharp.Resolver;
+using ICSharpCode.NRefactory.TypeSystem.Implementation;
+using System.Diagnostics;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
@@ -40,7 +43,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
#region ICodeIssueProvider implementation
public IEnumerable GetIssues(BaseRefactoringContext context)
{
- return new GatherVisitor(context, this).GetIssues();
+ var sw = new Stopwatch();
+ sw.Start();
+ var gatherer = new GatherVisitor(context, this);
+ var issues = gatherer.GetIssues().ToList();
+ sw.Stop();
+ Console.WriteLine("Elapsed time for ParameterCanBeDemotedIssue: {0} (resolved {2} method bodies in file '{1}')", sw.Elapsed, context.ParsedFile.FileName, gatherer.MethodResolveCount);
+ return issues;
}
#endregion
@@ -68,41 +77,62 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
methodDeclaration.AcceptVisitor(collector);
foreach (var parameter in methodDeclaration.Parameters) {
- var localResolveResult = context.Resolve(parameter) as LocalResolveResult;
- var variable = localResolveResult.Variable;
- var currentType = localResolveResult.Type;
- var candidateTypes = localResolveResult.Type.GetAllBaseTypes();
- var typeCriterion = collector.GetCriterion(variable);
- if (typeCriterion == null)
- // No usages in body
- return;
- var possibleTypes = GetPossibleTypes(candidateTypes, typeCriterion);
- var suggestedTypes = possibleTypes.Where(t => t != currentType);
- if (suggestedTypes.Any()) {
- AddIssue(parameter, context.TranslateString("Parameter can be demoted to base class"),
- GetActions(parameter, suggestedTypes));
- }
+ ProcessParameter(parameter, methodDeclaration.Body, collector);
}
}
+ void ProcessParameter(ParameterDeclaration parameter, BlockStatement body, TypeCriteriaCollector collector)
+ {
+ var directionExpression = parameter.Parent as DirectionExpression;
+ if (directionExpression != null && directionExpression.FieldDirection != FieldDirection.None)
+ // That kind of dependency is out of our control. Better not mess with it.
+ return;
+
+ var localResolveResult = context.Resolve(parameter) as LocalResolveResult;
+ var currentType = localResolveResult.Type;
+ var candidateTypes = localResolveResult.Type.GetAllBaseTypes();
+ var criterion = collector.GetCriterion(localResolveResult.Variable);
+ if (criterion == null)
+ // No usages in body
+ return;
+
+ var possibleTypes =
+ from type in candidateTypes
+ where criterion.SatisfiedBy(type) && TypeChangeResolvesCorrectly(parameter, body, type)
+ orderby GetInheritanceDepth(type) ascending
+ select type;
+
+ var suggestedTypes = possibleTypes.Where(type => !type.Equals(currentType));
+ if (suggestedTypes.Any()) {
+ AddIssue(parameter, context.TranslateString("Parameter can be demoted to base class"), GetActions(parameter, suggestedTypes));
+ }
+ }
+
+ internal int MethodResolveCount = 0;
+
+ bool TypeChangeResolvesCorrectly(ParameterDeclaration parameter, BlockStatement body, IType type)
+ {
+ MethodResolveCount++;
+ var resolver = context.GetResolverStateBefore(body);
+ resolver.AddVariable(new DefaultParameter(type, parameter.Name));
+ var astResolver = new CSharpAstResolver(resolver, body, context.ParsedFile);
+ var validator = new TypeChangeValidationNavigator();
+ astResolver.ApplyNavigator(validator, context.CancellationToken);
+ return !validator.FoundErrors;
+ }
+
IEnumerable GetActions(ParameterDeclaration parameter, IEnumerable possibleTypes)
{
+ var csResolver = context.Resolver.GetResolverStateBefore(parameter);
+ var astBuilder = new TypeSystemAstBuilder(csResolver);
foreach (var type in possibleTypes) {
- var typeName = type.Name;
- var message = string.Format("{0} '{1}'", context.TranslateString("Demote parameter to "), typeName);
+ var localType = type;
+ var message = string.Format(context.TranslateString("Demote parameter to '{0}'"), type.FullName);
yield return new CodeAction(message, script => {
- script.Replace(parameter.Type, new SimpleType(typeName));
+ script.Replace(parameter.Type, astBuilder.ConvertType(localType));
});
}
}
-
- IEnumerable GetPossibleTypes(IEnumerable types, ITypeCriterion criterion)
- {
- return from type in types
- where criterion.SatisfiedBy(type)
- orderby GetInheritanceDepth(type) ascending
- select type;
- }
int GetInheritanceDepth(IType declaringType)
{
@@ -114,6 +144,27 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return depth;
}
}
+
+ class TypeChangeValidationNavigator : IResolveVisitorNavigator
+ {
+ public bool FoundErrors { get; private set; }
+
+ #region IResolveVisitorNavigator implementation
+ public ResolveVisitorNavigationMode Scan(AstNode node)
+ {
+ return ResolveVisitorNavigationMode.Resolve;
+ }
+ public void Resolved(AstNode node, ResolveResult result)
+ {
+ FoundErrors |= result.IsError;
+ }
+ public void ProcessConversion(Expression expression, ResolveResult result, Conversion conversion, IType targetType)
+ {
+ // no-op
+ }
+ #endregion
+
+ }
}
}
diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterion.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterion.cs
new file mode 100644
index 0000000000..6b0aeef44d
--- /dev/null
+++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterion.cs
@@ -0,0 +1,107 @@
+//
+// SupportsIndexingCriterion.cs
+//
+// Author:
+// Simon Lindgren
+//
+// 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;
+using ICSharpCode.NRefactory.TypeSystem;
+using System.Collections.Generic;
+using ICSharpCode.NRefactory.TypeSystem.Implementation;
+using System.Linq;
+using ICSharpCode.NRefactory.Semantics;
+using ICSharpCode.NRefactory.CSharp.Resolver;
+
+namespace ICSharpCode.NRefactory.CSharp.Refactoring
+{
+ public class SupportsIndexingCriterion : ITypeCriterion
+ {
+ IType returnType;
+
+ IList argumentTypes;
+
+ CSharpConversions conversions;
+
+ bool isWriteAccess;
+
+ public SupportsIndexingCriterion(IType returnType, IEnumerable argumentTypes, CSharpConversions conversions, bool isWriteAccess = false)
+ {
+ if (returnType == null)
+ throw new ArgumentNullException("returnType");
+ if (argumentTypes == null)
+ throw new ArgumentNullException("argumentTypes");
+ if (conversions == null)
+ throw new ArgumentNullException("conversions");
+
+ this.returnType = returnType;
+ this.argumentTypes = argumentTypes.ToList();
+ this.conversions = conversions;
+ this.isWriteAccess = isWriteAccess;
+ }
+
+ #region ITypeCriterion implementation
+
+ public bool SatisfiedBy(IType type)
+ {
+ var accessors = type.GetAccessors().ToList();
+ return accessors.Any(member => {
+ var parameterizedMember = member as IParameterizedMember;
+ if (parameterizedMember == null)
+ return false;
+
+ if (isWriteAccess) {
+ var parameterCount = member.Parameters.Count;
+ if (member.Name != "set_Item" || parameterCount < 2)
+ return false;
+ var indexerElementType = parameterizedMember.Parameters.Last().Type;
+ var indexerParameterTypes = parameterizedMember.Parameters.Take(parameterCount - 1).Select(p => p.Type).ToList();
+ return IsSignatureMatch(indexerElementType, indexerParameterTypes);
+ } else {
+ if (member.Name != "get_Item" || member.Parameters.Count < 1)
+ return false;
+ var indexerElementType = parameterizedMember.ReturnType;
+ var indexerParameterTypes = parameterizedMember.Parameters.Select(p => p.Type).ToList();
+ return IsSignatureMatch(indexerElementType, indexerParameterTypes);
+ }
+ });
+ }
+
+ #endregion
+
+ bool IsSignatureMatch(IType indexerElementType, IList indexerParameterTypes)
+ {
+ indexerElementType.GetAllBaseTypes();
+ if (indexerParameterTypes.Count != argumentTypes.Count)
+ return false;
+ var returnConversion = conversions.ImplicitConversion(indexerElementType, returnType);
+ if (!returnConversion.IsValid)
+ return false;
+ for (int i = 0; i < argumentTypes.Count; i++) {
+ var conversion = conversions.ImplicitConversion(indexerParameterTypes[i], argumentTypes[i]);
+ if (!conversion.IsValid)
+ return false;
+ }
+ return true;
+ }
+ }
+}
+
diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/TypeCriteriaCollector.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/TypeCriteriaCollector.cs
index b7cff61dca..11eab29acb 100644
--- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/TypeCriteriaCollector.cs
+++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/TypeCriteriaCollector.cs
@@ -27,6 +27,7 @@ using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using System.Collections.Generic;
using System.Linq;
+using ICSharpCode.NRefactory.CSharp.Resolver;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
@@ -60,14 +61,52 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
base.VisitMemberReferenceExpression(memberReferenceExpression);
- var memberResolveResult = context.Resolve(memberReferenceExpression) as MemberResolveResult;
- if (memberResolveResult == null)
- return;
- var targetResolveResult = memberResolveResult.TargetResult as LocalResolveResult;
+ var targetResolveResult = context.Resolve(memberReferenceExpression.Target) as LocalResolveResult;
if (targetResolveResult == null)
return;
var variable = targetResolveResult.Variable;
- AddCriterion(variable, new HasMemberCriterion(memberResolveResult.Member));
+ var conversion = context.GetConversion(memberReferenceExpression);
+ if (conversion.IsMethodGroupConversion) {
+ AddCriterion(variable, new HasMemberCriterion(conversion.Method));
+ } else {
+ var resolveResult = context.Resolve(memberReferenceExpression);
+ var memberResolveResult = resolveResult as MemberResolveResult;
+ if (memberResolveResult != null)
+ AddCriterion(variable, new HasMemberCriterion(memberResolveResult.Member));
+ }
+ }
+
+ public override void VisitIndexerExpression(IndexerExpression indexerExpression)
+ {
+ base.VisitIndexerExpression(indexerExpression);
+
+ var localResolveResult = context.Resolve(indexerExpression.Target) as LocalResolveResult;
+ if (localResolveResult == null)
+ return;
+ var resolveResult = context.Resolve(indexerExpression);
+ if (localResolveResult == null)
+ return;
+ var parent = indexerExpression.Parent;
+ while (parent is ParenthesizedExpression)
+ parent = parent.Parent;
+ if (parent is DirectionExpression) {
+ // The only types which are indexable and where the indexing expression
+ // results in a variable is an actual array type
+ AddCriterion(localResolveResult.Variable, new IsArrayTypeCriterion());
+ } else if (resolveResult is ArrayAccessResolveResult) {
+ var arrayResolveResult = (ArrayAccessResolveResult)resolveResult;
+ var parameterTypes = arrayResolveResult.Indexes.Select(index => index.Type);
+ var arrayType = arrayResolveResult.Array.Type as ArrayType;
+ if (arrayType == null)
+ return;
+ var criterion = new SupportsIndexingCriterion(arrayType.ElementType, parameterTypes, CSharpConversions.Get(context.Compilation));
+ AddCriterion(localResolveResult.Variable, criterion);
+ } else if (resolveResult is CSharpInvocationResolveResult) {
+ var invocationResolveResult = (CSharpInvocationResolveResult)resolveResult;
+ var parameterTypes = invocationResolveResult.Arguments.Select(arg => arg.Type);
+ var criterion = new SupportsIndexingCriterion(invocationResolveResult.Member.ReturnType, parameterTypes, CSharpConversions.Get(context.Compilation));
+ AddCriterion(localResolveResult.Variable, criterion);
+ }
}
public override void VisitInvocationExpression(InvocationExpression invocationExpression)
@@ -78,27 +117,35 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
var invocationResolveResult = resolveResult as InvocationResolveResult;
if (invocationResolveResult == null)
return;
- CollectRestrictionsFromNodes(invocationExpression.Arguments);
// invocationExpression.Target resolves to a method group and VisitMemberReferenceExpression
// only handles members, so handle method groups here
var targetResolveResult = invocationResolveResult.TargetResult as LocalResolveResult;
- if (targetResolveResult == null)
- return;
- var variable = targetResolveResult.Variable;
- AddCriterion(variable, new HasMemberCriterion(invocationResolveResult.Member));
+ if (targetResolveResult != null) {
+ var variable = targetResolveResult.Variable;
+ AddCriterion(variable, new HasMemberCriterion(invocationResolveResult.Member));
+ }
}
- void CollectRestrictionsFromNodes(IEnumerable expressions)
+ protected override void VisitChildren(AstNode node)
{
- foreach (var expression in expressions) {
- var resolveResult = context.Resolve(expression);
- var argumentResolveResult = resolveResult as LocalResolveResult;
- if (argumentResolveResult == null)
- continue;
- var expectedType = context.GetExpectedType(expression);
- AddCriterion(argumentResolveResult.Variable, new IsTypeCriterion(expectedType));
+ if (node is Expression) {
+ CollectRestrictionsFromNodes((Expression)node);
}
+ base.VisitChildren(node);
+ }
+
+ void CollectRestrictionsFromNodes(Expression expression)
+ {
+ var role = expression.Role;
+ if (role != Roles.Expression && role != Roles.Argument)
+ return;
+ var resolveResult = context.Resolve(expression);
+ var localResolveResult = resolveResult as LocalResolveResult;
+ if (localResolveResult == null)
+ return;
+ var expectedType = context.GetExpectedType(expression);
+ AddCriterion(localResolveResult.Variable, new IsTypeCriterion(expectedType));
}
class ConjunctionCriteria : ITypeCriterion
diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs
index f18e35b91e..6d6d9041b5 100644
--- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs
+++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs
@@ -366,50 +366,138 @@ class Test
}
[Test]
- public void RespectsOutgoingCallsTypeRestrictionsWhenPassingResultFromMember()
+ public void AccountsForNonInvocationMethodGroupUsageInMethodCall()
{
var input = @"
-interface IA
+delegate void FooDelegate (string s);
+interface IBase
{
- void Foo(string s);
- string Property { get; }
+ void Bar();
}
-class B : IA
+interface IDerived : IBase
{
- public virtual void Foo(string s) {}
- public string Property { get; }
+ void Foo(string s);
}
class TestClass
{
- public void F(B b)
+ public void Bar (IDerived derived)
+ {
+ derived.Bar();
+ Baz (derived.Foo);
+ }
+
+ void Baz (FooDelegate fd)
{
- b.Foo(b.Property);
}
}";
TestRefactoringContext context;
var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context);
- Assert.AreEqual(1, issues.Count);
- var issue = issues [0];
- Assert.AreEqual(1, issue.Actions.Count);
- CheckFix(context, issues [0], @"
-interface IA
+ Assert.AreEqual(0, issues.Count);
+ }
+
+ [Test]
+ public void AccountsForNonInvocationMethodGroupUsageInVariableDeclaration()
+ {
+ var input = @"
+delegate void FooDelegate (string s);
+interface IBase
+{
+ void Bar();
+}
+interface IDerived : IBase
{
void Foo(string s);
- string Property { get; }
}
-class B : IA
+class TestClass
{
- public virtual void Foo(string s) {}
- public string Property { get; }
+ public void Bar (IDerived derived)
+ {
+ derived.Bar();
+ FooDelegate d = derived.Foo;
+ }
+}";
+ TestRefactoringContext context;
+ var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context);
+ Assert.AreEqual(0, issues.Count);
+ }
+
+ [Test]
+ public void AccountsForNonInvocationMethodGroupUsageInAssignmentExpression()
+ {
+ var input = @"
+delegate void FooDelegate (string s);
+interface IBase
+{
+ void Bar();
+}
+interface IDerived : IBase
+{
+ void Foo(string s);
}
class TestClass
{
- public void F(IA b)
+ public void Bar (IDerived derived)
{
- b.Foo(b.Property);
+ derived.Bar();
+ FooDelegate d;
+ d = derived.Foo;
+ }
+}";
+ TestRefactoringContext context;
+ var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context);
+ Assert.AreEqual(0, issues.Count);
+ }
+
+ [Test]
+ public void AccountsForIndexers()
+ {
+ var input = @"
+class TestClass
+{
+ void Write(string[] s)
+ {
+ object.Equals(s, s);
+ var element = s[1];
+ }
+}";
+ TestRefactoringContext context;
+ var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context);
+ Assert.AreEqual(1, issues.Count);
+ var issue = issues[0];
+ // Suggested types: IList and IReadOnlyList
+ Assert.AreEqual(2, issue.Actions.Count);
+
+ CheckFix(context, issues [0], @"
+class TestClass
+{
+ void Write(System.Collections.Generic.IList s)
+ {
+ object.Equals(s, s);
+ var element = s[1];
}
}");
}
+
+ [Test]
+ public void AccountsForArrays()
+ {
+ var input = @"
+class TestClass
+{
+ void Write(string[] s)
+ {
+ var i = s.Length;
+ SetValue (out s[1]);
+ }
+
+ void SetValue (out string s)
+ {
+ }
+}";
+ TestRefactoringContext context;
+ var issues = GetIssues(new ParameterCanBeDemotedIssue(), input, out context);
+ Assert.AreEqual(0, issues.Count);
+ }
}
}
diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterionTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterionTests.cs
new file mode 100644
index 0000000000..ab2d36dd31
--- /dev/null
+++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterionTests.cs
@@ -0,0 +1,94 @@
+//
+// SupportsIndexingCriterionTests.cs
+//
+// Author:
+// Simon Lindgren
+//
+// 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;
+using NUnit.Framework;
+using ICSharpCode.NRefactory.CSharp.CodeActions;
+using ICSharpCode.NRefactory.TypeSystem;
+using System.Collections.Generic;
+using ICSharpCode.NRefactory.CSharp.Refactoring;
+using ICSharpCode.NRefactory.CSharp.Resolver;
+
+namespace ICSharpCode.NRefactory.CSharp.CodeIssues
+{
+ [TestFixture]
+ public class SupportsIndexingCriterionTests
+ {
+ ITypeResolveContext typeResolveContext;
+
+ IType intType;
+
+ ICompilation compilation;
+
+ [SetUp]
+ public void SetUp()
+ {
+ compilation = TestRefactoringContext.Create("").Compilation;
+ typeResolveContext = compilation.TypeResolveContext;
+ intType = GetIType();
+ }
+
+ IType GetIType()
+ {
+ return typeof(T).ToTypeReference().Resolve(typeResolveContext);
+ }
+
+ void AssertMatches(IType candidateType, IType elementType, bool isWriteAccess, params IType[] indexTypes)
+ {
+ var criterion = new SupportsIndexingCriterion(elementType, indexTypes, CSharpConversions.Get(compilation), isWriteAccess);
+ Assert.IsTrue(criterion.SatisfiedBy(candidateType));
+ }
+
+ void AssertDoesNotMatch(IType candidateType, IType elementType, bool isWriteAccess, params IType[] indexTypes)
+ {
+ var criterion = new SupportsIndexingCriterion(elementType, indexTypes, CSharpConversions.Get(compilation), isWriteAccess);
+ Assert.IsFalse(criterion.SatisfiedBy(candidateType));
+ }
+
+ [Test]
+ public void ListWithOneIntegerIndex()
+ {
+ var intListType = GetIType>();
+ AssertMatches(intListType, intType, false, intType);
+ AssertMatches(intListType, intType, true, intType);
+ }
+
+ [Test]
+ public void ListWithTwoIntegerIndexes()
+ {
+ var intListType = GetIType>();
+ AssertDoesNotMatch(intListType, intType, false, intType, intType);
+ AssertDoesNotMatch(intListType, intType, true, intType, intType);
+ }
+
+ [Test]
+ public void ObjectCandidate()
+ {
+ AssertDoesNotMatch(GetIType