Browse Source

[CodeIssues] ParameterCanBeDemotedIssue: Add Criterions for suitable indexer members and for array types. Also start resolving the method with the new type to check for errors.

newNRvisualizers
Simon Lindgren 14 years ago
parent
commit
79b4c25ccc
  1. 2
      ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  2. 9
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/HasMemberCriterion.cs
  3. 43
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/IsArrayTypeCriterion.cs
  4. 103
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs
  5. 107
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterion.cs
  6. 83
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/TypeCriteriaCollector.cs
  7. 128
      ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs
  8. 94
      ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterionTests.cs
  9. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

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

@ -431,6 +431,8 @@ @@ -431,6 +431,8 @@
<Compile Include="Parser\SeekableStreamReader.cs" />
<Compile Include="Refactoring\CodeActions\ConvertAnonymousDelegateToLambdaAction.cs" />
<Compile Include="Refactoring\CodeActions\ConvertLambdaToAnonymousDelegateAction.cs" />
<Compile Include="Refactoring\CodeIssues\ParameterCanBeDemotedIssue\IsArrayTypeCriterion.cs" />
<Compile Include="Refactoring\CodeIssues\ParameterCanBeDemotedIssue\SupportsIndexingCriterion.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">

9
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/HasMemberCriterion.cs

@ -61,17 +61,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -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<T>(this IEnumerable<T> collection, IEnumerable<T> items)
static bool ContainsAny<T>(IEnumerable<T> collection, IEnumerable<T> items)
{
foreach (var item in items) {
if (collection.Contains(item))

43
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/IsArrayTypeCriterion.cs

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
//
// IsArrayTypeCriterion.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;
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
}
}

103
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedIssue.cs

@ -28,6 +28,9 @@ using ICSharpCode.NRefactory.TypeSystem; @@ -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 @@ -40,7 +43,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
#region ICodeIssueProvider implementation
public IEnumerable<CodeIssue> 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 @@ -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<CodeAction> GetActions(ParameterDeclaration parameter, IEnumerable<IType> 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<IType> GetPossibleTypes(IEnumerable<IType> 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 @@ -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
}
}
}

107
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterion.cs

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
//
// SupportsIndexingCriterion.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;
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<IType> argumentTypes;
CSharpConversions conversions;
bool isWriteAccess;
public SupportsIndexingCriterion(IType returnType, IEnumerable<IType> 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<IType> 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;
}
}
}

83
ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/ParameterCanBeDemotedIssue/TypeCriteriaCollector.cs

@ -27,6 +27,7 @@ using ICSharpCode.NRefactory.TypeSystem; @@ -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 @@ -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 @@ -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<Expression> 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

128
ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/ParameterCanBeDemotedTests.cs

@ -366,50 +366,138 @@ class Test @@ -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<T> and IReadOnlyList<T>
Assert.AreEqual(2, issue.Actions.Count);
CheckFix(context, issues [0], @"
class TestClass
{
void Write(System.Collections.Generic.IList<string> 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);
}
}
}

94
ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterCanBeDemotedIssue/SupportsIndexingCriterionTests.cs

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
//
// SupportsIndexingCriterionTests.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;
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<int>();
}
IType GetIType<T>()
{
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<IList<int>>();
AssertMatches(intListType, intType, false, intType);
AssertMatches(intListType, intType, true, intType);
}
[Test]
public void ListWithTwoIntegerIndexes()
{
var intListType = GetIType<IList<int>>();
AssertDoesNotMatch(intListType, intType, false, intType, intType);
AssertDoesNotMatch(intListType, intType, true, intType, intType);
}
[Test]
public void ObjectCandidate()
{
AssertDoesNotMatch(GetIType<object>(), intType, false, intType);
AssertDoesNotMatch(GetIType<object>(), intType, true, intType);
}
}
}

1
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -301,6 +301,7 @@ @@ -301,6 +301,7 @@
<Compile Include="CSharp\CodeIssues\StaticFieldInGenericTypeTests.cs" />
<Compile Include="CSharp\CodeActions\ConvertAnonymousDelegateToLambdaTests.cs" />
<Compile Include="CSharp\CodeActions\ConvertLamdaToAnonymousDelegateTests.cs" />
<Compile Include="CSharp\CodeIssues\ParameterCanBeDemotedIssue\SupportsIndexingCriterionTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">

Loading…
Cancel
Save