Browse Source

Add 'CommonTypeInference'.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
24eb146c74
  1. 8
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/SimpleNameLookupTests.cs
  2. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  3. 74
      ICSharpCode.NRefactory.Tests/TypeSystem/CommonTypeInferenceTests.cs
  4. 6
      ICSharpCode.NRefactory/CSharp/Dom/Expressions/AssignmentExpression.cs
  5. 4
      ICSharpCode.NRefactory/CSharp/Dom/Expressions/UnaryOperatorExpression.cs
  6. 2
      ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs
  7. 38
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  8. 4
      ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
  9. 4
      ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs
  10. 1
      ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs
  11. 23
      ICSharpCode.NRefactory/CSharp/Resolver/UnknownMemberResolveResult.cs
  12. 5
      ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  13. 342
      ICSharpCode.NRefactory/TypeSystem/CommonTypeInference.cs
  14. 16
      ICSharpCode.NRefactory/TypeSystem/IConversions.cs
  15. 2
      ICSharpCode.NRefactory/TypeSystem/IMember.cs
  16. 2
      ICSharpCode.NRefactory/TypeSystem/INamedElement.cs
  17. 12
      ICSharpCode.NRefactory/Utils/TreeTraversal.cs
  18. 5
      README

8
ICSharpCode.NRefactory.Tests/CSharp/Resolver/SimpleNameLookupTests.cs

@ -51,6 +51,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -51,6 +51,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Assert.AreEqual("System.String", trr.Type.FullName);
}
[Test]
public void UnknownIdentifierTest()
{
UnknownIdentifierResolveResult uirr = (UnknownIdentifierResolveResult)resolver.ResolveSimpleName("xyz", new IType[0]);
Assert.IsTrue(uirr.IsError);
Assert.AreEqual("xyz", uirr.Identifier);
}
[Test]
public void GlobalIsUnknownIdentifier()
{

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

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

74
ICSharpCode.NRefactory.Tests/TypeSystem/CommonTypeInferenceTests.cs

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
// Copyright (c) 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.CSharp.Resolver;
using NUnit.Framework;
namespace ICSharpCode.NRefactory.TypeSystem
{
[TestFixture]
public class CommonTypeInferenceTests
{
CommonTypeInference cti = new CommonTypeInference(CecilLoaderTests.Mscorlib, new Conversions(CecilLoaderTests.Mscorlib));
IType[] Resolve(params Type[] types)
{
IType[] r = new IType[types.Length];
for (int i = 0; i < types.Length; i++) {
r[i] = types[i].ToTypeReference().Resolve(CecilLoaderTests.Mscorlib);
Assert.AreNotSame(r[i], SharedTypes.UnknownType);
}
Array.Sort(r, (a,b)=>a.ReflectionName.CompareTo(b.ReflectionName));
return r;
}
IType[] CommonBaseTypes(params Type[] types)
{
return cti.CommonBaseTypes(Resolve(types)).OrderBy(r => r.ReflectionName).ToArray();
}
[Test]
public void ListOfStringAndObject()
{
Assert.AreEqual(
Resolve(typeof(IList), typeof(IEnumerable<object>)),
CommonBaseTypes(typeof(List<string>), typeof(List<object>)));
}
[Test]
public void ListOfListOfStringAndObject()
{
Assert.AreEqual(
Resolve(typeof(IList), typeof(IEnumerable<IList>), typeof(IEnumerable<IEnumerable<object>>)),
CommonBaseTypes(typeof(List<List<string>>), typeof(List<List<object>>)));
}
[Test]
public void ShortAndInt()
{
Assert.AreEqual(
Resolve(typeof(int)),
CommonBaseTypes(typeof(short), typeof(int)));
}
[Test]
public void ListOfShortAndInt()
{
Assert.AreEqual(
Resolve(typeof(IList)),
CommonBaseTypes(typeof(List<short>), typeof(List<int>)));
}
[Test]
public void StringAndVersion()
{
Assert.AreEqual(
Resolve(typeof(ICloneable), typeof(IComparable)),
CommonBaseTypes(typeof(string), typeof(Version)));
}
}
}

6
ICSharpCode.NRefactory/CSharp/Dom/Expressions/AssignmentExpression.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
//
//
// AssignmentExpression.cs
//
// Author:
@ -75,12 +75,12 @@ namespace ICSharpCode.NRefactory.CSharp @@ -75,12 +75,12 @@ namespace ICSharpCode.NRefactory.CSharp
/// <summary>left %= right</summary>
Modulus,
/// <summary>left <<= right</summary>
/// <summary>left &lt;&lt;= right</summary>
ShiftLeft,
/// <summary>left >>= right</summary>
ShiftRight,
/// <summary>left &= right</summary>
/// <summary>left &amp;= right</summary>
BitwiseAnd,
/// <summary>left |= right</summary>
BitwiseOr,

4
ICSharpCode.NRefactory/CSharp/Dom/Expressions/UnaryOperatorExpression.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
//
//
// UnaryOperatorExpression.cs
//
// Author:
@ -68,7 +68,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -68,7 +68,7 @@ namespace ICSharpCode.NRefactory.CSharp
PostDecrement,
/// <summary>Dereferencing (*a)</summary>
Dereference,
/// <summary>Get address (&a)</summary>
/// <summary>Get address (&amp;a)</summary>
AddressOf
}

2
ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs

@ -184,7 +184,7 @@ namespace ICSharpCode.NRefactory.CSharp @@ -184,7 +184,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
/// <summary>
/// Adds the 'Invoke', 'BeginInvoke', 'EndInvoke' methods, and a constructor, to the <see cref="delegateType"/>.
/// Adds the 'Invoke', 'BeginInvoke', 'EndInvoke' methods, and a constructor, to the <paramref name="delegateType"/>.
/// </summary>
public static void AddDefaultMethodsToDelegate(DefaultTypeDefinition delegateType, ITypeReference returnType, IEnumerable<IParameter> parameters)
{

38
ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -7,6 +7,7 @@ using System.Diagnostics; @@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
@ -22,13 +23,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -22,13 +23,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
static readonly ResolveResult NullResult = new ResolveResult(SharedTypes.Null);
readonly ITypeResolveContext context;
internal readonly CancellationToken cancellationToken;
#region Constructor
public CSharpResolver(ITypeResolveContext context)
public CSharpResolver(ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
this.cancellationToken = cancellationToken;
}
#endregion
@ -335,6 +338,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -335,6 +338,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region ResolveUnaryOperator method
public ResolveResult ResolveUnaryOperator(UnaryOperatorType op, ResolveResult expression)
{
cancellationToken.ThrowIfCancellationRequested();
if (expression.Type == SharedTypes.Dynamic)
return DynamicResult;
@ -584,6 +589,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -584,6 +589,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region ResolveBinaryOperator method
public ResolveResult ResolveBinaryOperator(BinaryOperatorType op, ResolveResult lhs, ResolveResult rhs)
{
cancellationToken.ThrowIfCancellationRequested();
if (lhs.Type == SharedTypes.Dynamic || rhs.Type == SharedTypes.Dynamic)
return DynamicResult;
@ -1427,6 +1434,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1427,6 +1434,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region ResolveCast
public ResolveResult ResolveCast(IType targetType, ResolveResult expression)
{
cancellationToken.ThrowIfCancellationRequested();
// C# 4.0 spec: §7.7.6 Cast expressions
if (expression.IsCompileTimeConstant) {
TypeCode code = ReflectionHelper.GetTypeCode(targetType);
@ -1504,8 +1513,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1504,8 +1513,12 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
}
return LookupSimpleNameOrTypeName(identifier, typeArguments,
isInvocationTarget ? SimpleNameLookupMode.InvocationTarget : SimpleNameLookupMode.Expression);
ResolveResult rr = LookupSimpleNameOrTypeName(
identifier, typeArguments,
isInvocationTarget ? SimpleNameLookupMode.InvocationTarget : SimpleNameLookupMode.Expression);
if (rr == ErrorResult && typeArguments.Count == 0)
rr = new UnknownIdentifierResolveResult(identifier);
return rr;
}
public ResolveResult LookupSimpleNamespaceOrTypeName(string identifier, IList<IType> typeArguments, bool isUsingDeclaration = false)
@ -1523,6 +1536,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1523,6 +1536,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
// C# 4.0 spec: §3.8 Namespace and type names; §7.6.2 Simple Names
cancellationToken.ThrowIfCancellationRequested();
int k = typeArguments.Count;
// look in type parameters of current method
@ -1629,6 +1644,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1629,6 +1644,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
if (identifier == "global")
return new NamespaceResolveResult(string.Empty);
for (UsingScope n = this.UsingScope; n != null; n = n.Parent) {
if (n.ExternAliases.Contains(identifier)) {
return ResolveExternAlias(identifier);
@ -1654,6 +1670,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1654,6 +1670,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
// C# 4.0 spec: §7.6.4
cancellationToken.ThrowIfCancellationRequested();
NamespaceResolveResult nrr = target as NamespaceResolveResult;
if (nrr != null) {
string fullName = NamespaceDeclaration.BuildQualifiedName(nrr.NamespaceName, identifier);
@ -1684,6 +1702,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1684,6 +1702,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null)
{
// C# 4.0 spec: §7.6.5
cancellationToken.ThrowIfCancellationRequested();
if (target.Type == SharedTypes.Dynamic)
return DynamicResult;
@ -1703,6 +1724,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1703,6 +1724,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (umrr != null) {
return new UnknownMethodResolveResult(umrr.TargetType, umrr.MemberName, umrr.TypeArguments, CreateParameters(arguments, argumentNames));
}
UnknownIdentifierResolveResult uirr = target as UnknownIdentifierResolveResult;
if (uirr != null && CurrentTypeDefinition != null) {
return new UnknownMethodResolveResult(CurrentTypeDefinition, uirr.Identifier, EmptyList<IType>.Instance, CreateParameters(arguments, argumentNames));
}
IMethod invokeMethod = target.Type.GetDelegateInvokeMethod();
if (invokeMethod != null) {
return new ResolveResult(invokeMethod.ReturnType.Resolve(context));
@ -1795,6 +1820,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1795,6 +1820,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region ResolveIndexer
public ResolveResult ResolveIndexer(ResolveResult target, ResolveResult[] arguments, string[] argumentNames = null)
{
cancellationToken.ThrowIfCancellationRequested();
if (target.Type == SharedTypes.Dynamic)
return DynamicResult;
@ -1818,6 +1845,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1818,6 +1845,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region ResolveObjectCreation
public ResolveResult ResolveObjectCreation(IType type, ResolveResult[] arguments, string[] argumentNames = null)
{
cancellationToken.ThrowIfCancellationRequested();
OverloadResolution or = new OverloadResolution(context, arguments, argumentNames, new IType[0]);
MemberLookup lookup = CreateMemberLookup();
bool allowProtectedAccess = lookup.AllowProtectedAccess(type);
@ -1904,6 +1933,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1904,6 +1933,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public ResolveResult ResolveConditional(ResolveResult trueExpression, ResolveResult falseExpression)
{
// C# 4.0 spec §7.14: Conditional operator
cancellationToken.ThrowIfCancellationRequested();
Conversions c = new Conversions(context);
bool isValid;
IType resultType;

4
ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs

@ -11,7 +11,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -11,7 +11,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary>
/// Contains logic that determines whether an implicit conversion exists between two types.
/// </summary>
public class Conversions
public class Conversions : IConversions
{
readonly ITypeResolveContext context;
readonly IType objectType;
@ -163,7 +163,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -163,7 +163,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#endregion
#region ImplicitReferenceConversion
bool ImplicitReferenceConversion(IType fromType, IType toType)
public bool ImplicitReferenceConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.6

4
ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs

@ -54,9 +54,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -54,9 +54,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary>
/// Gets whether <paramref name="entity"/> is accessible in the current class.
/// </summary>
/// <param name="member">The entity to test</param>
/// <param name="entity">The entity to test</param>
/// <param name="allowProtectedAccess">Whether protected access is allowed.
/// True if the type of the reference is derived from the current class.</returns>
/// True if the type of the reference is derived from the current class.</param>
public bool IsAccessible(IEntity entity, bool allowProtectedAccess)
{
if (entity == null)

1
ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs

@ -5,6 +5,7 @@ using System; @@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;

23
ICSharpCode.NRefactory/CSharp/Resolver/UnknownMemberResolveResult.cs

@ -65,9 +65,32 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -65,9 +65,32 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public ReadOnlyCollection<IParameter> Parameters {
get { return parameters; }
}
}
/// <summary>
/// Represents an unknown identifier.
/// </summary>
public class UnknownIdentifierResolveResult : ResolveResult
{
readonly string identifier;
public UnknownIdentifierResolveResult(string identifier)
: base(SharedTypes.UnknownType)
{
this.identifier = identifier;
}
public string Identifier {
get { return identifier; }
}
public override bool IsError {
get { return true; }
}
public override string ToString()
{
return string.Format("[{0} {1}]", GetType().Name, identifier);
}
}
}

5
ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj

@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -39,7 +40,7 @@ @@ -39,7 +40,7 @@
<Optimize>false</Optimize>
<WarningLevel>4</WarningLevel>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DebugSymbols>True</DebugSymbols>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>None</DebugType>
@ -189,12 +190,14 @@ @@ -189,12 +190,14 @@
<Compile Include="TypeSystem\ByReferenceType.cs" />
<Compile Include="TypeSystem\CecilLoader.cs" />
<Compile Include="TypeSystem\ClassType.cs" />
<Compile Include="TypeSystem\CommonTypeInference.cs" />
<Compile Include="TypeSystem\DomRegion.cs" />
<Compile Include="TypeSystem\EntityType.cs" />
<Compile Include="TypeSystem\ExtensionMethods.cs" />
<Compile Include="TypeSystem\IAccessor.cs" />
<Compile Include="TypeSystem\IAttribute.cs" />
<Compile Include="TypeSystem\IConstantValue.cs" />
<Compile Include="TypeSystem\IConversions.cs" />
<Compile Include="TypeSystem\IEntity.cs" />
<Compile Include="TypeSystem\IEvent.cs" />
<Compile Include="TypeSystem\IExplicitInterfaceImplementation.cs" />

342
ICSharpCode.NRefactory/TypeSystem/CommonTypeInference.cs

@ -0,0 +1,342 @@ @@ -0,0 +1,342 @@
// Copyright (c) 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.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace ICSharpCode.NRefactory.TypeSystem
{
/// <summary>
/// Inference engine for common base type / common super type.
/// This is not used in the C# resolver, as C# does not have this kind of powerful type inference.
/// The logic used by C# is implemented in <see cref="CSharp.Resolver.TypeInference.GetBestCommonType"/>.
///
/// This inference engine is intended for use in Refactorings.
/// </summary>
public sealed class CommonTypeInference
{
// The algorithm used is loosely based an extended version of the corresponding Java algorithm.
// The Java specifiction calls this 'lub' (least upper bound), and is defined only in one direction
// (for the other direction, Java uses intersection types).
//
// An improved algorithm for Java is presented in:
// Daniel Smith and Robert Cartwright. Java Type Inference Is Broken: Can We
// Fix It? In OOPSLA ’08: Proceedings of the 23rd ACM SIGPLAN conference
// on Object-oriented programming systems languages and applications pages 505–524,
// New York, NY, USA, 2008.
//
// The algorithm used here is losely based on that, although of course there are major differences:
// C# does not have any equivalent to Java's 'recursive types'
// (e.g. Comparable<? extends Comparable<? extends ...>>, nested infinitely),
// so a large part of the problematic cases disappear.
//
// However, C# also does not have any kind of intersection or union types.
// This means we have to find an approximation to such types, for which there might
// not be a unique solution.
// We use the term Union(T,S) to denote a type X for which T <: X and S <: X.
// X is a common base type of T and S.
// (if we had union types, the union "T|S" would be the most specific possible type X).
// We use the term Intersect(T,S) to denote a type X for which X <: T and X <: S.
// X is a common subtype of T and S.
// (if we had intersection types, the intersection "T|S" would be the least specific possible type X).
// Some examples to show the possible common base types:
// Union(List<string>, List<object>) = {
// IEnumerable<Union(string, object)> = IEnumerable<object>,
// IList,
// ICollection,
// IEnumerable,
// object,
// }
// Removing entries that are uninteresting as they as less specific than other existing entries, the result
// of Union(List<string>, List<object>) is { IEnumerable<object>, IList }.
// The number of options can be extremely high, especially when dealing with common subtypes.
// Intersect(IDisposable, ICloneable) will return all classes that implement both interfaces.
// In fact, for certain kinds of class declarations, there will be an infinite number of options.
// For this reason, this algorithm supports aborting the operation, either after a specific number of options
// has been found; or using a CancellationToken (e.g. when user clicks Cancel, or simply when too much time has expired).
readonly ITypeResolveContext context;
readonly IConversions conversions;
/// <summary>
/// Creates a new CommonTypeInference instance.
/// </summary>
/// <param name="context">The type resolve context to use.</param>
/// <param name="conversions">The language-specified conversion rules to use.</param>
public CommonTypeInference(ITypeResolveContext context, IConversions conversions)
{
if (context == null)
throw new ArgumentNullException("context");
if (conversions == null)
throw new ArgumentNullException("conversions");
this.context = context;
this.conversions = conversions;
}
public IEnumerable<IType> CommonBaseTypes(IList<IType> inputTypes, bool useOnlyReferenceConversion = false)
{
if (inputTypes == null)
throw new ArgumentNullException("inputTypes");
if (inputTypes.Count == 0)
return EmptyList<IType>.Instance;
// First test whether there is a type in the input that all other input types are convertible to
IType potentialCommonBaseType = inputTypes[0];
for (int i = 1; i < inputTypes.Count; i++) {
if (useOnlyReferenceConversion) {
if (conversions.ImplicitReferenceConversion(inputTypes[i], potentialCommonBaseType)) {
// OK, continue
} else if (conversions.ImplicitReferenceConversion(potentialCommonBaseType, inputTypes[i])) {
potentialCommonBaseType = inputTypes[i];
} else {
potentialCommonBaseType = null;
break;
}
} else {
if (conversions.ImplicitConversion(inputTypes[i], potentialCommonBaseType)) {
// OK, continue
} else if (conversions.ImplicitConversion(potentialCommonBaseType, inputTypes[i])) {
potentialCommonBaseType = inputTypes[i];
} else {
potentialCommonBaseType = null;
break;
}
}
}
if (potentialCommonBaseType != null)
return new[] { potentialCommonBaseType };
// If we're supposed to only use reference conversions, but there is a non-reference type left in the input,
// we can give up.
if (useOnlyReferenceConversion && inputTypes.Any(t => t.IsReferenceType != true))
return EmptyList<IType>.Instance;
// Debug output: input values
Debug.WriteLine("CommonBaseTypes input = {");
Debug.Indent();
foreach (IType type in inputTypes)
Debug.WriteLine(type);
Debug.Unindent();
Debug.WriteLine("}");
Dictionary<ITypeDefinition, TP[]> dict = new Dictionary<ITypeDefinition, TP[]>();
HashSet<IType> potentialTypes = new HashSet<IType>();
// Retrieve the initial candidates from the first bound
// generic types go to dict, non-generic types directly go to potentialTypes
foreach (IType baseType in inputTypes[0].GetAllBaseTypes(context)) {
ParameterizedType pt = baseType as ParameterizedType;
if (pt != null) {
TP[] tp = new TP[pt.TypeParameterCount];
for (int i = 0; i < tp.Length; i++) {
tp[i] = new TP(pt.GetDefinition().TypeParameters[i]);
tp[i].Bounds.Add(pt.TypeArguments[i]);
}
dict[pt.GetDefinition()] = tp;
} else {
potentialTypes.Add(baseType);
}
}
// Now retrieve candidates for all other bounds, and intersect the different sets of candidates.
for (int i = 1; i < inputTypes.Count; i++) {
IEnumerable<IType> baseTypesForThisBound = inputTypes[i].GetAllBaseTypes(context);
HashSet<ITypeDefinition> genericTypeDefsForThisLowerBound = new HashSet<ITypeDefinition>();
foreach (IType baseType in baseTypesForThisBound) {
ParameterizedType pt = baseType as ParameterizedType;
if (pt != null) {
TP[] tp;
if (dict.TryGetValue(pt.GetDefinition(), out tp)) {
genericTypeDefsForThisLowerBound.Add(pt.GetDefinition());
for (int j = 0; j < tp.Length; j++) {
tp[j].Bounds.Add(pt.TypeArguments[j]);
}
}
}
}
potentialTypes.IntersectWith(baseTypesForThisBound);
foreach (ITypeDefinition def in dict.Keys.ToArray()) {
if (!genericTypeDefsForThisLowerBound.Contains(def))
dict.Remove(def);
}
}
// Now figure out the generic types, and add them to potential types if possible.
foreach (var pair in dict) {
Debug.WriteLine("CommonBaseTypes: " + pair.Key);
Debug.Indent();
IType[][] typeArguments = new IType[pair.Value.Length][];
bool error = false;
for (int i = 0; i < pair.Value.Length; i++) {
var tp = pair.Value[i];
Debug.WriteLine("Fixing " + tp);
Debug.Indent();
switch (tp.Variance) {
case VarianceModifier.Covariant:
typeArguments[i] = CommonBaseTypes(tp.Bounds.ToArray(), true).ToArray();
break;
case VarianceModifier.Contravariant:
typeArguments[i] = CommonSubTypes(tp.Bounds.ToArray(), true).ToArray();
break;
default: // Invariant
if (tp.Bounds.Count == 1)
typeArguments[i] = new IType[] { tp.Bounds.Single() };
break;
}
Debug.Unindent();
if (typeArguments[i] == null || typeArguments[i].Length == 0) {
Debug.WriteLine(" -> error");
error = true;
break;
} else {
Debug.WriteLine(" -> " + string.Join(",", typeArguments[i].AsEnumerable()));
}
}
if (!error) {
foreach (IType[] ta in AllCombinations(typeArguments)) {
IType result = new ParameterizedType(pair.Key, ta);
Debug.WriteLine("Result: " + result);
potentialTypes.Add(result);
}
}
Debug.Unindent();
}
// Debug output: list candidates found so far:
Debug.WriteLine("CommonBaseTypes candidates = {");
Debug.Indent();
foreach (IType type in potentialTypes)
Debug.WriteLine(type);
Debug.Unindent();
Debug.WriteLine("}");
// Remove redundant types
foreach (IType type in potentialTypes.ToArray()) {
bool isRedundant = false;
foreach (IType otherType in potentialTypes) {
if (type != otherType && conversions.ImplicitReferenceConversion(otherType, type)) {
isRedundant = true;
break;
}
}
if (isRedundant)
potentialTypes.Remove(type);
}
return potentialTypes;
}
/// <summary>
/// Performs the combinatorial explosion.
/// </summary>
IEnumerable<IType[]> AllCombinations(IType[][] typeArguments)
{
int[] index = new int[typeArguments.Length];
index[typeArguments.Length - 1] = -1;
while (true) {
int i;
for (i = index.Length - 1; i >= 0; i--) {
if (++index[i] == typeArguments[i].Length)
index[i] = 0;
else
break;
}
if (i < 0)
break;
IType[] r = new IType[typeArguments.Length];
for (i = 0; i < r.Length; i++) {
r[i] = typeArguments[i][index[i]];
}
yield return r;
}
}
public IEnumerable<IType> CommonSubTypes(IList<IType> inputTypes, bool useOnlyReferenceConversion = false)
{
if (inputTypes == null)
throw new ArgumentNullException("inputTypes");
if (inputTypes.Count == 0)
return EmptyList<IType>.Instance;
// First test whether there is a type in the input that can be converted to all other input types
IType potentialCommonSubType = inputTypes[0];
for (int i = 1; i < inputTypes.Count; i++) {
if (useOnlyReferenceConversion) {
if (conversions.ImplicitReferenceConversion(potentialCommonSubType, inputTypes[i])) {
// OK, continue
} else if (conversions.ImplicitReferenceConversion(inputTypes[i], potentialCommonSubType)) {
potentialCommonSubType = inputTypes[i];
} else {
potentialCommonSubType = null;
break;
}
} else {
if (conversions.ImplicitConversion(potentialCommonSubType, inputTypes[i])) {
// OK, continue
} else if (conversions.ImplicitConversion(inputTypes[i], potentialCommonSubType)) {
potentialCommonSubType = inputTypes[i];
} else {
potentialCommonSubType = null;
break;
}
}
}
if (potentialCommonSubType != null)
return new[] { potentialCommonSubType };
// Now we're left with the open-ended quest to find a type that derives from all input types.
return new IType[0];
}
sealed class TP
{
public readonly VarianceModifier Variance;
public TP(ITypeParameter tp)
{
this.Variance = tp.Variance;
}
public readonly HashSet<IType> Bounds = new HashSet<IType>();
#if DEBUG
public override string ToString()
{
StringBuilder b = new StringBuilder();
b.Append('(');
if (this.Variance == VarianceModifier.Covariant) {
bool first = true;
foreach (IType type in Bounds) {
if (first) first = false; else b.Append(" | ");
b.Append(type);
}
b.Append(" <: ");
}
b.Append("TP");
if (this.Variance == VarianceModifier.Contravariant) {
b.Append(" <: ");
bool first = true;
foreach (IType type in Bounds) {
if (first) first = false; else b.Append(" & ");
b.Append(type);
}
} else if (this.Variance == VarianceModifier.Invariant) {
foreach (IType type in Bounds) {
b.Append(" = ");
b.Append(type);
}
}
b.Append(')');
return b.ToString();
}
#endif
}
}
}

16
ICSharpCode.NRefactory/TypeSystem/IConversions.cs

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
// Copyright (c) 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;
namespace ICSharpCode.NRefactory.TypeSystem
{
/// <summary>
/// Interface used to check whether types are convertible.
/// </summary>
public interface IConversions
{
bool ImplicitConversion(IType fromType, IType toType);
bool ImplicitReferenceConversion(IType fromType, IType toType);
}
}

2
ICSharpCode.NRefactory/TypeSystem/IMember.cs

@ -16,7 +16,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -16,7 +16,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// <summary>
/// Gets/Sets the declaring type (incl. type arguments, if any).
/// This property never returns null -- for top-level members, it returns SharedTypes.UnknownType.
/// If this is not a specialized member, the value returned is equal to <see cref="DeclaringTypeDefinition"/>.
/// If this is not a specialized member, the value returned is equal to <see cref="IEntity.DeclaringTypeDefinition"/>.
/// </summary>
IType DeclaringType { get; }

2
ICSharpCode.NRefactory/TypeSystem/INamedElement.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -50,7 +50,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
/// <remarks>
/// For types, the reflection name can be parsed back into a ITypeReference by using
/// <see cref="ReflectionHelper.ParseReflectionName"/>.
/// <see cref="ReflectionHelper.ParseReflectionName(string,IEntity)"/>.
/// </remarks>
/// <returns>
/// "System.Int32[]" for int[]<br/>

12
ICSharpCode.NRefactory/Utils/TreeTraversal.cs

@ -14,8 +14,8 @@ namespace ICSharpCode.NRefactory.Utils @@ -14,8 +14,8 @@ namespace ICSharpCode.NRefactory.Utils
/// <summary>
/// Converts a tree data structure into a flat list by traversing it in pre-order.
/// </summary>
/// <param name="input">The root element of the tree.</param>
/// <param name="recursive">The function that gets the children of an element.</param>
/// <param name="root">The root element of the tree.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in pre-order.</returns>
public static IEnumerable<T> PreOrder<T>(T root, Func<T, IEnumerable<T>> recursion)
{
@ -26,7 +26,7 @@ namespace ICSharpCode.NRefactory.Utils @@ -26,7 +26,7 @@ namespace ICSharpCode.NRefactory.Utils
/// Converts a tree data structure into a flat list by traversing it in pre-order.
/// </summary>
/// <param name="input">The root elements of the forest.</param>
/// <param name="recursive">The function that gets the children of an element.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in pre-order.</returns>
public static IEnumerable<T> PreOrder<T>(IEnumerable<T> input, Func<T, IEnumerable<T>> recursion)
{
@ -54,8 +54,8 @@ namespace ICSharpCode.NRefactory.Utils @@ -54,8 +54,8 @@ namespace ICSharpCode.NRefactory.Utils
/// <summary>
/// Converts a tree data structure into a flat list by traversing it in post-order.
/// </summary>
/// <param name="input">The root element of the tree.</param>
/// <param name="recursive">The function that gets the children of an element.</param>
/// <param name="root">The root element of the tree.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in post-order.</returns>
public static IEnumerable<T> PostOrder<T>(T root, Func<T, IEnumerable<T>> recursion)
{
@ -66,7 +66,7 @@ namespace ICSharpCode.NRefactory.Utils @@ -66,7 +66,7 @@ namespace ICSharpCode.NRefactory.Utils
/// Converts a tree data structure into a flat list by traversing it in post-order.
/// </summary>
/// <param name="input">The root elements of the forest.</param>
/// <param name="recursive">The function that gets the children of an element.</param>
/// <param name="recursion">The function that gets the children of an element.</param>
/// <returns>Iterator that enumerates the tree structure in post-order.</returns>
public static IEnumerable<T> PostOrder<T>(IEnumerable<T> input, Func<T, IEnumerable<T>> recursion)
{

5
README

@ -172,7 +172,8 @@ A: They don't use any particular format. They're merely intended as a debugging @@ -172,7 +172,8 @@ A: They don't use any particular format. They're merely intended as a debugging
Currently .ToString() usually matches .ReflectionName, but that may change in the future.
Q: Why are there extension methods IType.IsEnum() and IType.IsDelegate(), but no IType.IsStruct()' or IType.IsInterface()?
Q: Why are there extension methods IType.IsEnum() and IType.IsDelegate(), but no IType.IsStruct()
or IType.IsInterface()?
A: Because if you're asking whether a type is a struct, it's very likely that you're asking the
wrong question.
@ -181,7 +182,7 @@ A: Because if you're asking whether a type is a struct, it's very likely that yo @@ -181,7 +182,7 @@ A: Because if you're asking whether a type is a struct, it's very likely that yo
so important in the world of types.
If whatever you are doing works with struct-types, then it likely will also work with
enum-types, and also with type parameters constraint to be a value-type.
enum-types, and also with type parameters with a value-type constraint.
So instead of asking IsStruct(), you really should be asking: IType.IsReferenceType == false
Enums and delegates are special because you can do special things with those types

Loading…
Cancel
Save