Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add 'CommonTypeInference'.

  • Loading branch information...
commit 24eb146c74d86523ca612893296a4cee565e1819 1 parent 0eeedc4
@dgrunwald dgrunwald authored
Showing with 525 additions and 24 deletions.
  1. +8 −0 ICSharpCode.NRefactory.Tests/CSharp/Resolver/SimpleNameLookupTests.cs
  2. +1 −0  ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  3. +74 −0 ICSharpCode.NRefactory.Tests/TypeSystem/CommonTypeInferenceTests.cs
  4. +3 −3 ICSharpCode.NRefactory/CSharp/Dom/Expressions/AssignmentExpression.cs
  5. +2 −2 ICSharpCode.NRefactory/CSharp/Dom/Expressions/UnaryOperatorExpression.cs
  6. +1 −1  ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs
  7. +35 −3 ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  8. +2 −2 ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
  9. +2 −2 ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs
  10. +1 −0  ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs
  11. +23 −0 ICSharpCode.NRefactory/CSharp/Resolver/UnknownMemberResolveResult.cs
  12. +4 −1 ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
  13. +342 −0 ICSharpCode.NRefactory/TypeSystem/CommonTypeInference.cs
  14. +16 −0 ICSharpCode.NRefactory/TypeSystem/IConversions.cs
  15. +1 −1  ICSharpCode.NRefactory/TypeSystem/IMember.cs
  16. +1 −1  ICSharpCode.NRefactory/TypeSystem/INamedElement.cs
  17. +6 −6 ICSharpCode.NRefactory/Utils/TreeTraversal.cs
  18. +3 −2 README
View
8 ICSharpCode.NRefactory.Tests/CSharp/Resolver/SimpleNameLookupTests.cs
@@ -52,6 +52,14 @@ public void ImportedType()
}
[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()
{
Assert.IsTrue(resolver.ResolveSimpleName("global", new IType[0]).IsError);
View
1  ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
@@ -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" />
View
74 ICSharpCode.NRefactory.Tests/TypeSystem/CommonTypeInferenceTests.cs
@@ -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)));
+ }
+ }
+}
View
6 ICSharpCode.NRefactory/CSharp/Dom/Expressions/AssignmentExpression.cs
@@ -1,4 +1,4 @@
-//
+//
// AssignmentExpression.cs
//
// Author:
@@ -75,12 +75,12 @@ public enum AssignmentOperatorType
/// <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,
View
4 ICSharpCode.NRefactory/CSharp/Dom/Expressions/UnaryOperatorExpression.cs
@@ -1,4 +1,4 @@
-//
+//
// UnaryOperatorExpression.cs
//
// Author:
@@ -68,7 +68,7 @@ public enum UnaryOperatorType
PostDecrement,
/// <summary>Dereferencing (*a)</summary>
Dereference,
- /// <summary>Get address (&a)</summary>
+ /// <summary>Get address (&amp;a)</summary>
AddressOf
}
View
2  ICSharpCode.NRefactory/CSharp/Parser/TypeSystemConvertVisitor.cs
@@ -184,7 +184,7 @@ static IParameter MakeParameter(ITypeReference type, string name)
}
/// <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)
{
View
38 ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
@@ -7,6 +7,7 @@
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 @@ public class CSharpResolver
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 @@ public override string ToString()
#region ResolveUnaryOperator method
public ResolveResult ResolveUnaryOperator(UnaryOperatorType op, ResolveResult expression)
{
+ cancellationToken.ThrowIfCancellationRequested();
+
if (expression.Type == SharedTypes.Dynamic)
return DynamicResult;
@@ -584,6 +589,8 @@ object GetUserUnaryOperatorCandidates()
#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 @@ object GetUserBinaryOperatorCandidates()
#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 @@ public ResolveResult ResolveSimpleName(string identifier, IList<IType> typeArgum
}
}
- 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 @@ ResolveResult LookupSimpleNameOrTypeName(string identifier, IList<IType> typeArg
{
// 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 @@ public ResolveResult ResolveAlias(string identifier)
{
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 @@ public ResolveResult ResolveMemberAccess(ResolveResult target, string identifier
{
// 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 @@ MemberLookup CreateMemberLookup()
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 @@ public ResolveResult ResolveInvocation(ResolveResult target, ResolveResult[] arg
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 @@ static string MakeParameterName(string variableName)
#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 @@ public ResolveResult ResolveIndexer(ResolveResult target, ResolveResult[] argume
#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 @@ public ResolveResult ResolveBaseReference()
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;
View
4 ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
@@ -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 @@ bool NullLiteralConversion(IType fromType, IType toType)
#endregion
#region ImplicitReferenceConversion
- bool ImplicitReferenceConversion(IType fromType, IType toType)
+ public bool ImplicitReferenceConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §6.1.6
View
4 ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs
@@ -54,9 +54,9 @@ public bool AllowProtectedAccess(IType targetType)
/// <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)
View
1  ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using System.Threading;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
View
23 ICSharpCode.NRefactory/CSharp/Resolver/UnknownMemberResolveResult.cs
@@ -65,9 +65,32 @@ public UnknownMethodResolveResult(IType targetType, string methodName, IEnumerab
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);
+ }
}
}
View
5 ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj
@@ -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 @@
<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 @@
<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" />
View
342 ICSharpCode.NRefactory/TypeSystem/CommonTypeInference.cs
@@ -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
+ }
+ }
+}
View
16 ICSharpCode.NRefactory/TypeSystem/IConversions.cs
@@ -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);
+ }
+}
View
2  ICSharpCode.NRefactory/TypeSystem/IMember.cs
@@ -16,7 +16,7 @@ public interface IMember : IEntity
/// <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; }
View
2  ICSharpCode.NRefactory/TypeSystem/INamedElement.cs
@@ -50,7 +50,7 @@ public interface INamedElement
/// </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/>
View
12 ICSharpCode.NRefactory/Utils/TreeTraversal.cs
@@ -14,8 +14,8 @@ public static class TreeTraversal
/// <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 @@ public static IEnumerable<T> PreOrder<T>(T root, Func<T, IEnumerable<T>> recursi
/// 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 @@ public static IEnumerable<T> PreOrder<T>(IEnumerable<T> input, Func<T, IEnumerab
/// <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 @@ public static IEnumerable<T> PostOrder<T>(T root, Func<T, IEnumerable<T>> recurs
/// 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)
{
View
5 README
@@ -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
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
Please sign in to comment.
Something went wrong with that request. Please try again.