Browse Source

Improved overload lookup (but the rules from § 14.4.2.2 are still missing).

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@444 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 20 years ago
parent
commit
d135e0bd4f
  1. 16
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs
  2. 3
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/ExpressionFinder.cs
  3. 32
      src/AddIns/BackendBindings/CSharpBinding/Test/ExpressionFinder.cs
  4. 16
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs
  5. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  6. 2
      src/Main/Base/Project/Src/Dom/Implementations/DefaultClass.cs
  7. 2
      src/Main/Base/Project/Src/Dom/Implementations/SpecificReturnType.cs
  8. 406
      src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs
  9. 4
      src/Main/Base/Project/Src/Dom/NRefactoryResolver/NRefactoryResolver.cs
  10. 81
      src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs
  11. 12
      src/Main/Base/Project/Src/Dom/ReflectionLayer/ReflectionReturnType.cs
  12. 24
      src/Main/Base/Test/GenericResolverTests.cs

16
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs

@ -137,7 +137,21 @@ namespace CSharpBinding @@ -137,7 +137,21 @@ namespace CSharpBinding
parameterTypes[i] = rr.ResolvedType;
}
}
dp.DefaultIndex = TypeVisitor.FindOverload(methods, parameterTypes, false, out overloadIsSure);
int[] ranking = MemberLookupHelper.RankOverloads(methods, parameterTypes, true, out overloadIsSure);
bool multipleBest = false;
int bestRanking = -1;
int best = 0;
for (int i = 0; i < ranking.Length; i++) {
if (ranking[i] > bestRanking) {
bestRanking = ranking[i];
best = i;
multipleBest = false;
} else if (ranking[i] == bestRanking) {
multipleBest = true;
}
}
if (multipleBest) overloadIsSure = false;
dp.DefaultIndex = best;
}
editor.ShowInsightWindow(dp);
if (overloadIsSure) {

3
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Parser/ExpressionFinder.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// <file>
// <file>
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
// <license see="prj:///doc/license.txt">GNU General Public License</license>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
@ -664,6 +664,7 @@ namespace CSharpBinding.Parser @@ -664,6 +664,7 @@ namespace CSharpBinding.Parser
break;
case "return":
case "throw":
case "in":
// treat as error / end of expression
break;
default:

32
src/AddIns/BackendBindings/CSharpBinding/Test/ExpressionFinder.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// <file>
// <file>
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
// <license see="prj:///doc/license.txt">GNU General Public License</license>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
@ -30,6 +30,15 @@ class Main<T> : BaseType @@ -30,6 +30,15 @@ class Main<T> : BaseType
}
}";
const string program2 = @"using System;
class Main {
void Method() {
foreach (TypeName varName in ((CastTo)castTarget).PropertyOnCastExpression) {
}
}
}";
ExpressionFinder ef;
[SetUp]
@ -40,9 +49,14 @@ class Main<T> : BaseType @@ -40,9 +49,14 @@ class Main<T> : BaseType
void FindFull(string location, string expectedExpression, ExpressionContext expectedContext)
{
int pos = document.IndexOf(location);
if (pos < 0) Assert.Fail("location not found in document");
ExpressionResult er = ef.FindFullExpression(document, pos);
FindFull(document, location, expectedExpression, expectedContext);
}
void FindFull(string program, string location, string expectedExpression, ExpressionContext expectedContext)
{
int pos = program.IndexOf(location);
if (pos < 0) Assert.Fail("location not found in program");
ExpressionResult er = ef.FindFullExpression(program, pos);
Assert.AreEqual(expectedExpression, er.Expression);
Assert.AreEqual(expectedContext.ToString(), er.Context.ToString());
}
@ -84,15 +98,21 @@ class Main<T> : BaseType @@ -84,15 +98,21 @@ class Main<T> : BaseType
}
[Test]
public void Method()
public void MethodOnCast()
{
FindFull("thodOnCastExpression(para", "((CastTo)castTarget).MethodOnCastExpression(parameter)", ExpressionContext.Default);
}
[Test]
public void Property()
public void PropertyOnCast()
{
FindFull("pertyOnCastExpression", "((CastTo)castTarget).PropertyOnCastExpression", ExpressionContext.Default);
}
[Test]
public void PropertyOnCastInForeachLoop()
{
FindFull(program2, "pertyOnCastExpression", "((CastTo)castTarget).PropertyOnCastExpression", ExpressionContext.Default);
}
}
}

16
src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetCompletionBinding.cs

@ -142,7 +142,21 @@ namespace VBNetBinding @@ -142,7 +142,21 @@ namespace VBNetBinding
parameterTypes[i] = rr.ResolvedType;
}
}
dp.DefaultIndex = TypeVisitor.FindOverload(methods, parameterTypes, false, out overloadIsSure);
int[] ranking = MemberLookupHelper.RankOverloads(methods, parameterTypes, true, out overloadIsSure);
bool multipleBest = false;
int bestRanking = -1;
int best = 0;
for (int i = 0; i < ranking.Length; i++) {
if (ranking[i] > bestRanking) {
bestRanking = ranking[i];
best = i;
multipleBest = false;
} else if (ranking[i] == bestRanking) {
multipleBest = true;
}
}
if (multipleBest) overloadIsSure = false;
dp.DefaultIndex = best;
}
editor.ShowInsightWindow(dp);
if (overloadIsSure) {

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -689,6 +689,7 @@ @@ -689,6 +689,7 @@
<Compile Include="Src\Services\RefactoringService\RefactoringMenuBuilder.cs" />
<EmbeddedResource Include="Resources\FullscreenPanel.xfrm" />
<Compile Include="Src\Gui\Dialogs\OptionPanels\IDEOptions\FullscreenPanel.cs" />
<Compile Include="Src\Dom\MemberLookupHelper.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Libraries\DockPanel_Src\WinFormsUI\WinFormsUI.csproj">

2
src/Main/Base/Project/Src/Dom/Implementations/DefaultClass.cs

@ -284,7 +284,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -284,7 +284,7 @@ namespace ICSharpCode.SharpDevelop.Dom
case ClassType.Enum:
return ProjectContentRegistry.Mscorlib.GetClass("System.Enum").DefaultReturnType;
case ClassType.Delegate:
return ProjectContentRegistry.Mscorlib.GetClass("System.Delegate").DefaultReturnType;
return ReflectionReturnType.Delegate;
case ClassType.Struct:
return ProjectContentRegistry.Mscorlib.GetClass("System.ValueType").DefaultReturnType;
}

2
src/Main/Base/Project/Src/Dom/Implementations/SpecificReturnType.cs

@ -114,7 +114,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -114,7 +114,7 @@ namespace ICSharpCode.SharpDevelop.Dom
}
}
public static IReturnType TranslateType(IReturnType input, List<IReturnType> typeParameters, bool convertForMethod)
public static IReturnType TranslateType(IReturnType input, IList<IReturnType> typeParameters, bool convertForMethod)
{
if (input is GenericReturnType) {
GenericReturnType rt = (GenericReturnType)input;

406
src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs

@ -0,0 +1,406 @@ @@ -0,0 +1,406 @@
// <file>
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
// <license see="prj:///doc/license.txt">GNU General Public License</license>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections;
using System.Collections.Generic;
using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// Class with methods to help finding the correct overload for a member.
/// </summary>
/// <remarks>
/// This class tries to do member lookup as close to the C# spec (ECMA-334, § 14.3) as possible.
/// Other languages might need custom lookup methods.
/// </remarks>
public static class MemberLookupHelper
{
#region FindOverload
public static IMethod FindOverload(IList<IMethod> methods, IReturnType[] typeParameters, IReturnType[] arguments)
{
if (methods.Count == 0)
return null;
bool tmp;
int[] ranking = RankOverloads(methods, typeParameters, arguments, false, out tmp);
int bestRanking = -1;
int best = 0;
for (int i = 0; i < ranking.Length; i++) {
if (ranking[i] > bestRanking) {
bestRanking = ranking[i];
best = i;
}
}
return methods[best];
}
public static IProperty FindOverload(IList<IProperty> properties, IReturnType[] arguments)
{
if (properties.Count == 0)
return null;
bool tmp;
List<IMethodOrProperty> newList = new List<IMethodOrProperty>(properties.Count);
foreach (IProperty p in properties) newList.Add(p);
int[] ranking = RankOverloads(newList, arguments, false, out tmp);
int bestRanking = -1;
int best = 0;
for (int i = 0; i < ranking.Length; i++) {
if (ranking[i] > bestRanking) {
bestRanking = ranking[i];
best = i;
}
}
return properties[best];
}
#endregion
#region Rank method overloads
/// <summary>
/// Assigns a ranking score to each method in the <paramref name="list"/>.
/// </summary>
/// <param name="list">Link with the methods to check.<br/>
/// <b>Generic methods in the input type are replaced by methods with have the types substituted!</b>
/// </param>
/// <param name="typeParameters">List with the type parameters passed to the method.
/// Can be null (=no type parameters)</param>
/// <param name="arguments">The types of the arguments passed to the method.
/// A null return type means any argument type is allowed.</param>
/// <param name="allowAdditionalArguments">Specifies whether the method can have
/// more parameters than specified here. Useful for method insight scenarios.</param>
/// <param name="acceptableMatch">Out parameter that is true when the best ranked
/// method is acceptable for a method call (no invalid casts)</param>
/// <returns>Integer array. Each value in the array </returns>
public static int[] RankOverloads(IList<IMethod> list,
IReturnType[] typeParameters,
IReturnType[] arguments,
bool allowAdditionalArguments,
out bool acceptableMatch)
{
acceptableMatch = false;
if (list.Count == 0) return new int[] {};
List<IMethodOrProperty> l2 = new List<IMethodOrProperty>(list.Count);
// See ECMA-334, § 14.3
// If type parameters are specified, remove all methods from the list that do not
// use the specified number of parameters.
if (typeParameters != null && typeParameters.Length > 0) {
for (int i = 0; i < list.Count; i++) {
IMethod m = list[i];
if (m.TypeParameters.Count == typeParameters.Length) {
m = (IMethod)m.Clone();
m.ReturnType = SpecificReturnType.TranslateType(m.ReturnType, typeParameters, true);
for (int j = 0; j < m.Parameters.Count; ++j) {
m.Parameters[j].ReturnType = SpecificReturnType.TranslateType(m.Parameters[j].ReturnType, typeParameters, true);
}
list[i] = m;
l2.Add(m);
}
}
int[] innerRanking = RankOverloads(l2, arguments, allowAdditionalArguments, out acceptableMatch);
int[] ranking = new int[list.Count];
int innerIndex = 0;
for (int i = 0; i < ranking.Length; i++) {
if (list[i].TypeParameters.Count == typeParameters.Length) {
ranking[i] = innerRanking[innerIndex++];
} else {
ranking[i] = 0;
}
}
return ranking;
} else {
// Note that when there are no type parameters, methods having type parameters
// are not removed, since the type inference process might be able to infer the
// type arguments.
foreach (IMethod m in list) l2.Add(m);
return RankOverloads(l2, arguments, allowAdditionalArguments, out acceptableMatch);
}
}
#endregion
#region Main ranking algorithm
/// <summary>
/// The inner ranking engine. Works on both methods and properties.
/// For parameter documentation, read the comments on the above method.
/// </summary>
public static int[] RankOverloads(IList<IMethodOrProperty> list,
IReturnType[] arguments,
bool allowAdditionalArguments,
out bool acceptableMatch)
{
acceptableMatch = false;
if (list.Count == 0) return new int[] {};
int[] ranking = new int[list.Count];
bool[] needToExpand = new bool[list.Count];
int maxScore = 0;
int baseScore = 0;
for (int i = 0; i < list.Count; i++) {
int score;
bool expanded;
if (IsApplicable(list[i].Parameters, arguments, allowAdditionalArguments, out score, out expanded)) {
acceptableMatch = true;
score = int.MaxValue;
} else {
baseScore = Math.Max(baseScore, score);
}
needToExpand[i] = expanded;
ranking[i] = score;
maxScore = Math.Max(maxScore, score);
}
// all overloads that have maxScore (normally those that are applicable)
// have to be rescored.
// The new scala starts with baseScore + 1 to ensure that all applicable members have
// a higher score than non-applicable members
// The first step is to expand the methods and do type argument substitution
// TODO: Continue writing the member lookup stuff!
return ranking;
}
#endregion
#region IsApplicable
static bool IsApplicable(List<IParameter> parameters,
IReturnType[] arguments,
bool allowAdditionalArguments,
out int score,
out bool expanded)
{
// see ECMA-334, § 14.4.2.1
// TODO: recognize ref/out (needs info about passing mode for arguments, you have to introduce RefReturnType)
expanded = false;
score = 0;
if (parameters.Count == 0)
return arguments.Length == 0;
if (!allowAdditionalArguments && parameters.Count > arguments.Length + 1)
return false;
int lastParameter = parameters.Count - 1;
// check all arguments except the last
bool ok = true;
for (int i = 0; i < Math.Min(lastParameter, arguments.Length); i++) {
if (IsApplicable(arguments[i], parameters[i].ReturnType)) {
score++;
} else {
ok = false;
}
}
if (!ok) {
return false;
}
if (parameters.Count == arguments.Length) {
// try if method is applicable in normal form by checking last argument
if (IsApplicable(arguments[lastParameter], parameters[lastParameter].ReturnType)) {
return true;
}
}
// method is not applicable in normal form, try expanded form:
// - last parameter must be params array
if (!parameters[lastParameter].IsParams) {
return false;
}
expanded = true;
score++;
// - all additional parameters must be applicable to the unpacked array
ArrayReturnType rt = parameters[lastParameter].ReturnType as ArrayReturnType;
if (rt == null) {
return false;
}
for (int i = lastParameter; i < arguments.Length; i++) {
if (IsApplicable(arguments[i], rt.ElementType)) {
score++;
} else {
ok = false;
}
}
return ok;
}
static bool IsApplicable(IReturnType argument, IReturnType expected)
{
if (argument == null) // TODO: Use NullReturnType instead of no return type
return true; // "null" can be passed for any argument
if (expected is GenericReturnType) {
foreach (IReturnType constraint in ((GenericReturnType)expected).TypeParameter.Constraints) {
if (!ConversionExists(argument, constraint)) {
return false;
}
}
}
return ConversionExists(argument, expected);
}
#endregion
#region Conversion exists
/// <summary>
/// Checks if an implicit conversion exists from <paramref name="from"/> to <paramref name="to"/>.
/// </summary>
public static bool ConversionExists(IReturnType from, IReturnType to)
{
// ECMA-334, § 13.1 Implicit conversions
// Identity conversion:
if (from.Equals(to)) {
return true;
}
bool fromIsDefault = from.IsDefaultReturnType;
bool toIsDefault = to.IsDefaultReturnType;
if (fromIsDefault && toIsDefault) {
// Implicit numeric conversions:
int f = GetPrimitiveType(from);
int t = GetPrimitiveType(to);
if (f == SByte && (t == Short || t == Int || t == Long || t == Float || t == Double || t == Decimal))
return true;
if (f == Byte && (t == Short || t == UShort || t == Int || t == UInt || t == Long || t == ULong || t == Float || t == Double || t == Decimal))
return true;
if (f == Short && (t == Int || t == Long || t == Float || t == Double || t == Decimal))
return true;
if (f == UShort && (t == Int || t == UInt || t == Long || t == ULong || t == Float || t == Double || t == Decimal))
return true;
if (f == Int && (t == Long || t == Float || t == Double || t == Decimal))
return true;
if (f == UInt && (t == Long || t == ULong || t == Float || t == Double || t == Decimal))
return true;
if ((f == Long || f == ULong) && (t == Float || t == Double || t == Decimal))
return true;
if (f == Char && (t == UShort || t == Int || t == UInt || t == Long || t == ULong || t == Float || t == Double || t == Decimal))
return true;
if (f == Float && t == Double)
return true;
}
// Implicit reference conversions:
if (toIsDefault && to.FullyQualifiedName == "System.Object") {
return true; // from any type to object
}
if (toIsDefault && (fromIsDefault || from.ArrayDimensions > 0)) {
IClass c1 = from.GetUnderlyingClass();
IClass c2 = to.GetUnderlyingClass();
if (c1.IsTypeInInheritanceTree(c2)) {
return true;
}
}
if (from is ArrayReturnType && to is ArrayReturnType && from.ArrayDimensions == to.ArrayDimensions) {
// from array to other array type
return ConversionExists((from as ArrayReturnType).ElementType, (to as ArrayReturnType).ElementType);
}
return false;
}
#endregion
#region Better conversion
/// <summary>
/// Gets if the conversion from <paramref name="from"/> to <paramref name="to1"/> is better than
/// the conversion from <paramref name="from"/> to <paramref name="to2"/>.
/// </summary>
/// <returns>
/// 0 = neither conversion is better<br/>
/// 1 = from -> to1 is the better conversion<br/>
/// 2 = from -> to2 is the better conversion.
/// </returns>
public static int GetBetterConversion(IReturnType from, IReturnType to1, IReturnType to2)
{
// See ECMA-334, § 14.4.2.3
// If T1 and T2 are the same type, neither conversion is better.
if (to1.Equals(to2)) {
return 0;
}
// If S is T1, C1 is the better conversion.
if (from.Equals(to1)) {
return 1;
}
// If S is T2, C2 is the better conversion.
if (from.Equals(to2)) {
return 2;
}
bool canConvertFrom1To2 = ConversionExists(to1, to2);
bool canConvertFrom2To1 = ConversionExists(to2, to1);
// If an implicit conversion from T1 to T2 exists, and no implicit conversion
// from T2 to T1 exists, C1 is the better conversion.
if (canConvertFrom1To2 && !canConvertFrom2To1) {
return 1;
}
// If an implicit conversion from T2 to T1 exists, and no implicit conversion
// from T1 to T2 exists, C2 is the better conversion.
if (canConvertFrom2To1 && !canConvertFrom1To2) {
return 2;
}
if (to1.IsDefaultReturnType && to2.IsDefaultReturnType) {
return GetBetterPrimitiveConversion(to1, to2);
}
// Otherwise, neither conversion is better.
return 0;
}
const int Byte = 1;
const int Short = 2;
const int Int = 3;
const int Long = 4;
const int SByte = 5;
const int UShort = 6;
const int UInt = 7;
const int ULong = 8;
const int Float = 9;
const int Double = 10;
const int Char = 11;
const int Decimal= 12;
static int GetBetterPrimitiveConversion(IReturnType to1, IReturnType to2)
{
int t1 = GetPrimitiveType(to1);
int t2 = GetPrimitiveType(to2);
if (t1 == 0 || t2 == 0) return 0; // not primitive
if (t1 == SByte && (t2 == Byte || t2 == UShort || t2 == UInt || t2 == ULong))
return 1;
if (t2 == SByte && (t1 == Byte || t1 == UShort || t1 == UInt || t1 == ULong))
return 2;
if (t1 == Short && (t2 == UShort || t2 == UInt || t2 == ULong))
return 1;
if (t2 == Short && (t1 == UShort || t1 == UInt || t1 == ULong))
return 2;
if (t1 == Int && (t2 == UInt || t2 == ULong))
return 1;
if (t2 == Int && (t1 == UInt || t1 == ULong))
return 2;
if (t1 == Long && t2 == ULong)
return 1;
if (t2 == Long && t1 == ULong)
return 2;
return 0;
}
static int GetPrimitiveType(IReturnType t)
{
switch (t.FullyQualifiedName) {
case "System.SByte": return SByte;
case "System.Byte": return Byte;
case "System.Int16": return Short;
case "System.UInt16": return UShort;
case "System.Int32": return Int;
case "System.UInt32": return UInt;
case "System.Int64": return Long;
case "System.UInt64": return ULong;
case "System.Single": return Float;
case "System.Double": return Double;
case "System.Char": return Char;
case "System.Decimal": return Decimal;
default: return 0;
}
}
#endregion
}
}

4
src/Main/Base/Project/Src/Dom/NRefactoryResolver/NRefactoryResolver.cs

@ -231,7 +231,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -231,7 +231,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
ctors.Add(m);
}
TypeVisitor typeVisitor = new TypeVisitor(this);
return CreateMemberResolveResult(typeVisitor.FindOverload(ctors, ie.Parameters, null));
return CreateMemberResolveResult(typeVisitor.FindOverload(ctors, null, ie.Parameters, null));
}
}
return null;
@ -325,7 +325,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -325,7 +325,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return CreateMemberResolveResult(Constructor.CreateDefault(c));
}
}
return CreateMemberResolveResult(typeVisitor.FindOverload(constructors, ((ObjectCreateExpression)expr).Parameters, null));
return CreateMemberResolveResult(typeVisitor.FindOverload(constructors, null, ((ObjectCreateExpression)expr).Parameters, null));
}
return new ResolveResult(callingClass, callingMember, type);
}

81
src/Main/Base/Project/Src/Dom/NRefactoryResolver/TypeVisitor.cs

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
// <file>
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
// <license see="prj:///doc/license.txt">GNU General Public License</license>
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
@ -40,6 +40,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -40,6 +40,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
{
switch (binaryOperatorExpression.Op) {
case BinaryOperatorType.AsCast:
case BinaryOperatorType.NullCoalescing:
return binaryOperatorExpression.Right.AcceptVisitor(this, data);
case BinaryOperatorType.DivideInteger:
return ReflectionReturnType.Int;
@ -97,23 +98,24 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -97,23 +98,24 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return null;
}
public IMethod FindOverload(List<IMethod> methods, ArrayList arguments, object data)
public IMethod FindOverload(List<IMethod> methods, IReturnType[] typeParameters, ArrayList arguments, object data)
{
if (methods.Count <= 0) {
return null;
}
if (methods.Count == 1)
return (IMethod)methods[0];
// We can't use this shortcut because MemberLookupHelper does type inference and
// type substitution for us
//if (methods.Count == 1)
// return methods[0];
IReturnType[] types = new IReturnType[arguments.Count];
for (int i = 0; i < types.Length; ++i) {
types[i] = ((Expression)arguments[i]).AcceptVisitor(this, data) as IReturnType;
}
bool tmp;
List<IMethodOrProperty> methodList = methods.ConvertAll<IMethodOrProperty>(delegate (IMethod m) { return m; });
return (IMethod)methods[FindOverload(methodList, types, true, out tmp)];
return MemberLookupHelper.FindOverload(methods, typeParameters, types);
}
/*
/// <summary>
/// Finds the index of the overload in <paramref name="methods"/> that is the best
/// match for a call with the specified return types.
@ -153,6 +155,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -153,6 +155,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return bestIndex;
}
/// <summary>
/// Calculates a score how good the specified <paramref name="method"/> matches
/// the <paramref name="types"/>.
@ -191,23 +194,23 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -191,23 +194,23 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return -Math.Abs(method.Parameters.Count - types.Length);
}
}
*/
public IMethod GetMethod(InvocationExpression invocationExpression, object data)
{
IReturnType[] typeParameters = CreateReturnTypes(invocationExpression.TypeParameters);
if (invocationExpression.TargetObject is FieldReferenceExpression) {
FieldReferenceExpression field = (FieldReferenceExpression)invocationExpression.TargetObject;
IReturnType type = field.TargetObject.AcceptVisitor(this, data) as IReturnType;
List<IMethod> methods = resolver.SearchMethod(type, field.FieldName);
InjectMethodTypeParameters(methods, invocationExpression);
return FindOverload(methods, invocationExpression.Parameters, data);
return FindOverload(methods, typeParameters, invocationExpression.Parameters, data);
} else if (invocationExpression.TargetObject is IdentifierExpression) {
string id = ((IdentifierExpression)invocationExpression.TargetObject).Identifier;
if (resolver.CallingClass == null) {
return null;
}
List<IMethod> methods = resolver.SearchMethod(id);
InjectMethodTypeParameters(methods, invocationExpression);
return FindOverload(methods, invocationExpression.Parameters, data);
return FindOverload(methods, typeParameters, invocationExpression.Parameters, data);
}
return null;
}
@ -230,33 +233,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -230,33 +233,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
if (expr != null)
parameters[i] = (IReturnType)expr.AcceptVisitor(this, data);
}
bool tmp;
int num = FindOverload(new List<IMethodOrProperty>(indexers.ToArray()), parameters, true, out tmp);
if (num < 0)
return null;
else
return indexers[num];
}
void InjectMethodTypeParameters(List<IMethod> methods, InvocationExpression invocationExpression)
{
if (invocationExpression.TypeParameters == null) return;
if (invocationExpression.TypeParameters.Count == 0) return;
List<IReturnType> typeParameters = CreateReturnType(invocationExpression.TypeParameters);
for (int i = 0; i < methods.Count; ++i) {
IMethod m = methods[i] as IMethod;
if (m == null) continue; // ignore Events
if (m.TypeParameters.Count == 0) {
m = null; // this is not the correct overload, ignore this method
} else {
m = (IMethod)m.Clone();
m.ReturnType = SpecificReturnType.TranslateType(m.ReturnType, typeParameters, true);
for (int j = 0; j < m.Parameters.Count; ++j) {
m.Parameters[j].ReturnType = SpecificReturnType.TranslateType(m.Parameters[j].ReturnType, typeParameters, true);
}
}
methods[i] = m;
}
return MemberLookupHelper.FindOverload(indexers.ToArray(), parameters);
}
public override object Visit(FieldReferenceExpression fieldReferenceExpression, object data)
@ -455,10 +432,19 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -455,10 +432,19 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return type;
}
public override object Visit(DirectionExpression directionExpression, object data)
public override object Visit(TypeOfIsExpression typeOfIsExpression, object data)
{
// no calls allowed !!!
return null;
return ReflectionReturnType.Bool;
}
public override object Visit(DefaultValueExpression defaultValueExpression, object data)
{
return CreateReturnType(defaultValueExpression.TypeReference);
}
public override object Visit(AnonymousMethodExpression anonymousMethodExpression, object data)
{
return ReflectionReturnType.Delegate;
}
public override object Visit(ArrayInitializerExpression arrayInitializerExpression, object data)
@ -467,14 +453,19 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -467,14 +453,19 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return null;
}
List<IReturnType> CreateReturnType(List<TypeReference> references)
IReturnType CreateReturnType(TypeReference reference)
{
return references.ConvertAll<IReturnType>(CreateReturnType);
return CreateReturnType(reference, resolver);
}
IReturnType CreateReturnType(TypeReference reference)
IReturnType[] CreateReturnTypes(List<TypeReference> references)
{
return CreateReturnType(reference, resolver);
if (references == null) return new IReturnType[0];
IReturnType[] types = new IReturnType[references.Count];
for (int i = 0; i < types.Length; i++) {
types[i] = CreateReturnType(references[i]);
}
return types;
}
public static IReturnType CreateReturnType(TypeReference reference, NRefactoryResolver resolver)

12
src/Main/Base/Project/Src/Dom/ReflectionLayer/ReflectionReturnType.cs

@ -14,7 +14,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -14,7 +14,7 @@ namespace ICSharpCode.SharpDevelop.Dom
public static class ReflectionReturnType
{
#region Primitive Types
static IReturnType @object, @int, @string, @bool, type, @void, array, disposable, exception;
static IReturnType @object, @int, @string, @bool, type, @void, array, disposable, exception, @delegate;
/// <summary>Gets a ReturnType describing System.Object.</summary>
public static IReturnType Object {
@ -98,6 +98,16 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -98,6 +98,16 @@ namespace ICSharpCode.SharpDevelop.Dom
}
}
/// <summary>Gets a ReturnType describing System.Delegate.</summary>
public static IReturnType Delegate {
get {
if (@delegate == null) {
@delegate = CreatePrimitive(typeof(Delegate));
}
return @delegate;
}
}
/// <summary>
/// Create a primitive return type.
/// Allowed are ONLY simple classes from MsCorlib (no arrays/generics etc.)

24
src/Main/Base/Test/GenericResolverTests.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// <file>
// <file>
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
// <license see="prj:///doc/license.txt">GNU General Public License</license>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
@ -117,6 +117,28 @@ class TestClass { @@ -117,6 +117,28 @@ class TestClass {
Assert.AreEqual("TestClass.PublicField", mrr.ResolvedMember.FullyQualifiedName);
}
[Test]
public void TypeInferredGenericMethodCallTest()
{
ResolveResult result = Resolve(listProgram, "CloneIt(new TestClass())", 5);
Assert.IsNotNull(result);
Assert.IsTrue(result is MemberResolveResult);
Assert.AreEqual("TestClass", result.ResolvedType.FullyQualifiedName);
MemberResolveResult mrr = (MemberResolveResult) result;
Assert.AreEqual("TestClass.CloneIt", mrr.ResolvedMember.FullyQualifiedName);
}
[Test]
public void FieldReferenceOnTypeInferredGenericMethodCallTest()
{
ResolveResult result = Resolve(listProgram, "CloneIt(new TestClass()).PublicField", 5);
Assert.IsNotNull(result);
Assert.IsTrue(result is MemberResolveResult);
Assert.AreEqual("System.Int32", result.ResolvedType.FullyQualifiedName);
MemberResolveResult mrr = (MemberResolveResult) result;
Assert.AreEqual("TestClass.PublicField", mrr.ResolvedMember.FullyQualifiedName);
}
[Test]
public void ImportAliasClassResolveTest()
{

Loading…
Cancel
Save