Browse Source

Rewrite GetAllBaseTypes() to ensure the output is finite; and add unit tests for it.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
2853feea02
  1. 26
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs
  2. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  3. 91
      ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs
  4. 42
      ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs
  5. 30
      ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs

26
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs

@ -39,11 +39,37 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -39,11 +39,37 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
Assert.IsTrue(ImplicitConversion(typeof(char), typeof(char)));
Assert.IsTrue(ImplicitConversion(typeof(string), typeof(string)));
Assert.IsTrue(ImplicitConversion(typeof(object), typeof(object)));
Assert.IsFalse(ImplicitConversion(typeof(bool), typeof(char)));
Assert.IsTrue(conversions.ImplicitConversion(SharedTypes.Dynamic, SharedTypes.Dynamic));
Assert.IsTrue(conversions.ImplicitConversion(SharedTypes.UnknownType, SharedTypes.UnknownType));
Assert.IsTrue(conversions.ImplicitConversion(SharedTypes.Null, SharedTypes.Null));
}
[Test]
public void DynamicIdentityConversions()
{
Assert.IsTrue(ImplicitConversion(typeof(object), SharedTypes.Dynamic));
Assert.IsTrue(ImplicitConversion(SharedTypes.Dynamic, typeof(object)));
}
[Test]
public void ComplexDynamicIdentityConversions()
{
var listOfDynamic = new ParameterizedType(mscorlib.GetClass(typeof(List<>)), new [] { SharedTypes.Dynamic });
Assert.IsTrue(ImplicitConversion(typeof(List<object>), listOfDynamic));
Assert.IsTrue(ImplicitConversion(listOfDynamic, typeof(List<object>)));
Assert.IsFalse(ImplicitConversion(typeof(List<string>), listOfDynamic));
Assert.IsFalse(ImplicitConversion(listOfDynamic, typeof(List<string>)));
var complexTypeInvolvingDynamic = new ParameterizedType(mscorlib.GetClass(typeof(List<>)), new [] { new ArrayType(listOfDynamic, 1) });
// complexTypeInvolvingDynamic = List<List<dynamic>[]>
Assert.IsTrue(ImplicitConversion(complexTypeInvolvingDynamic, typeof(List<List<object>[]>)));
Assert.IsTrue(ImplicitConversion(typeof(List<List<object>[]>), complexTypeInvolvingDynamic));
Assert.IsFalse(ImplicitConversion(typeof(List<List<object>[,]>), complexTypeInvolvingDynamic));
}
[Test]
public void PrimitiveConversions()
{

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

@ -51,6 +51,7 @@ @@ -51,6 +51,7 @@
<Compile Include="FormattingTests\TestTypeLevelIndentation.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TypeSystem\CecilLoaderTests.cs" />
<Compile Include="TypeSystem\GetAllBaseTypesTest.cs" />
<Compile Include="TypeSystem\ReflectionHelperTests.cs" />
<Compile Include="TypeSystem\TestInterningProvider.cs" />
<Compile Include="TypeSystem\TypeSystemTests.cs" />

91
ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.TypeSystem
{
[TestFixture]
public class GetAllBaseTypesTest
{
IProjectContent mscorlib = CecilLoaderTests.Mscorlib;
ITypeResolveContext context = CecilLoaderTests.Mscorlib;
IType[] GetAllBaseTypes(Type type)
{
return type.ToTypeReference().Resolve(context).GetAllBaseTypes(context).OrderBy(t => t.DotNetName).ToArray();
}
IType[] GetTypes(params Type[] types)
{
return types.Select(t => t.ToTypeReference().Resolve(context)).OrderBy(t => t.DotNetName).ToArray();;
}
[Test]
public void ObjectBaseTypes()
{
Assert.AreEqual(GetTypes(typeof(object)), GetAllBaseTypes(typeof(object)));
}
[Test]
public void StringBaseTypes()
{
Assert.AreEqual(GetTypes(typeof(string), typeof(object), typeof(IComparable), typeof(ICloneable), typeof(IConvertible),
typeof(IComparable<string>), typeof(IEquatable<string>), typeof(IEnumerable<char>), typeof(IEnumerable)),
GetAllBaseTypes(typeof(string)));
}
[Test]
public void ClassDerivingFromItself()
{
// class C : C {}
DefaultTypeDefinition c = new DefaultTypeDefinition(mscorlib, string.Empty, "C");
c.BaseTypes.Add(c);
Assert.AreEqual(new [] { c }, c.GetAllBaseTypes(context).ToArray());
}
[Test]
public void TwoClassesDerivingFromEachOther()
{
// class C1 : C2 {} class C2 : C1 {}
DefaultTypeDefinition c1 = new DefaultTypeDefinition(mscorlib, string.Empty, "C1");
DefaultTypeDefinition c2 = new DefaultTypeDefinition(mscorlib, string.Empty, "C2");
c1.BaseTypes.Add(c2);
c2.BaseTypes.Add(c1);
Assert.AreEqual(new [] { c1, c2 }, c1.GetAllBaseTypes(context).ToArray());
}
[Test]
public void ClassDerivingFromParameterizedVersionOfItself()
{
// class C<X> : C<C<X>> {}
DefaultTypeDefinition c = new DefaultTypeDefinition(mscorlib, string.Empty, "C");
c.TypeParameters.Add(new DefaultTypeParameter(c, 0, "X"));
c.BaseTypes.Add(new ParameterizedType(c, new [] { new ParameterizedType(c, new [] { c.TypeParameters[0] }) }));
Assert.AreEqual(new [] { c }, c.GetAllBaseTypes(context).ToArray());
}
[Test]
public void ClassDerivingFromTwoInstanciationsOfIEnumerable()
{
// class C : IEnumerable<int>, IEnumerable<uint> {}
DefaultTypeDefinition c = new DefaultTypeDefinition(mscorlib, string.Empty, "C");
c.BaseTypes.Add(typeof(IEnumerable<int>).ToTypeReference());
c.BaseTypes.Add(typeof(IEnumerable<uint>).ToTypeReference());
Assert.AreEqual(new [] {
c,
c.BaseTypes[0].Resolve(context),
c.BaseTypes[1].Resolve(context),
mscorlib.GetClass(typeof(IEnumerable)),
mscorlib.GetClass(typeof(object))
},
c.GetAllBaseTypes(context).OrderBy(t => t.DotNetName).ToArray());
}
}
}

42
ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs

@ -18,21 +18,39 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -18,21 +18,39 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
/// <remarks>This is the reflexive and transitive closure of <see cref="IType.GetBaseTypes"/>.
/// Note that this method does not return all supertypes - doing so is impossible due to contravariance
/// (and underisable for covariance and the list could become very large).
/// This method may return an infinite list for certain (invalid) class declarations like <c>class C{T} : C{C{T}}</c>
/// TODO: ensure we never produce infinite lists (important for C# Conversions implementation)
/// (and undesirable for covariance as the list could become very large).
/// </remarks>
public static IEnumerable<IType> GetAllBaseTypes(this IType type, ITypeResolveContext context)
{
// Given types as nodes and GetBaseTypes() as edges, the type hierarchy forms a graph.
// This method should return all nodes reachable from the given start node.
// We perform this operation by converting the graph into a tree by making sure we return each node at most once.
// Then we convert the tree into a flat list using the Flatten operation.
HashSet<IType> visited = new HashSet<IType>();
visited.Add(type);
return TreeTraversal.PreOrder(type, t => t.GetBaseTypes(context).Where(visited.Add));
List<IType> output = new List<IType>();
Stack<ITypeDefinition> activeTypeDefinitions = new Stack<ITypeDefinition>();
CollectAllBaseTypes(type, context, activeTypeDefinitions, output);
return output;
}
static void CollectAllBaseTypes(IType type, ITypeResolveContext context, Stack<ITypeDefinition> activeTypeDefinitions, List<IType> output)
{
ITypeDefinition def = type.GetDefinition();
if (def != null) {
// Maintain a stack of currently active type definitions, and avoid having one definition
// multiple times on that stack.
// This is necessary to ensure the output is finite in the presence of cyclic inheritance:
// class C<X> : C<C<X>> {} would not be caught by the 'no duplicate output' check, yet would
// produce infinite output.
if (activeTypeDefinitions.Contains(def))
return;
activeTypeDefinitions.Push(def);
}
// Avoid outputting a type more than once - necessary for "diamond" multiple inheritance
// (e.g. C implements I1 and I2, and both interfaces derive from Object)
if (output.Contains(type))
return;
output.Add(type);
foreach (IType baseType in type.GetBaseTypes(context)) {
CollectAllBaseTypes(baseType, context, activeTypeDefinitions, output);
}
if (def != null)
activeTypeDefinitions.Pop();
}
}
}

30
ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs

@ -17,9 +17,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -17,9 +17,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// Example: List&lt;string&gt;
/// </summary>
/// <remarks>
/// When getting the Members, this type modifies the lists in such a way that the
/// <see cref="GenericReturnType"/>s are replaced with the return types in the
/// type arguments collection.
/// When getting the members, this type modifies the lists so that
/// type parameters in the signatures of the members are replaced with
/// the type arguments.
/// </remarks>
public sealed class ParameterizedType : Immutable, IType
{
@ -131,9 +131,9 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -131,9 +131,9 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
}
public IType GetElementType()
public override string ToString()
{
throw new NotSupportedException();
return DotNetName;
}
public ReadOnlyCollection<IType> TypeArguments {
@ -284,13 +284,23 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -284,13 +284,23 @@ namespace ICSharpCode.NRefactory.TypeSystem
ITypeDefinition def = g as ITypeDefinition;
if (def == null)
return g;
IType[] ta = new IType[typeArguments.Length];
bool isSame = g == genericType;
// Keep ta == null as long as no elements changed, allocate the array only if necessary.
IType[] ta = (g != genericType) ? new IType[typeArguments.Length] : null;
for (int i = 0; i < typeArguments.Length; i++) {
ta[i] = typeArguments[i].AcceptVisitor(visitor);
isSame &= ta[i] == typeArguments[i];
IType r = typeArguments[i].AcceptVisitor(visitor);
if (r == null)
throw new NullReferenceException("TypeVisitor.Visit-method returned null");
if (ta == null && r != typeArguments[i]) {
// we found a difference, so we need to allocate the array
ta = new IType[typeArguments.Length];
for (int j = 0; j < i; j++) {
ta[j] = typeArguments[j];
}
}
if (ta != null)
ta[i] = r;
}
if (isSame)
if (ta == null)
return this;
else
return new ParameterizedType(def, ta);

Loading…
Cancel
Save