Browse Source

Improved overload resolution by implementing 14.4.2.2 Better function member.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@454 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 20 years ago
parent
commit
da62a3f37c
  1. 4
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs
  2. 4
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs
  3. 4
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs
  4. 10
      src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs
  5. 145
      src/Main/Base/Project/Src/Dom/MemberLookupHelper.cs
  6. 8
      src/Main/Base/Project/Src/Gui/Components/StatusBar/SdStatusBar.cs
  7. 4
      src/Main/Base/Test/GenericResolverTests.cs
  8. 1
      src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj
  9. 14
      src/Main/Base/Test/NRefactoryResolverTests.cs
  10. 81
      src/Main/Base/Test/OverloadFinding.cs

4
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpAmbience.cs

@ -503,9 +503,9 @@ namespace ICSharpCode.Core @@ -503,9 +503,9 @@ namespace ICSharpCode.Core
ConstructedReturnType rt = (ConstructedReturnType)returnType;
UnpackNestedType(builder, rt.BaseType);
builder.Append('<');
for (int i = 0; i < rt.TypeParameters.Count; ++i) {
for (int i = 0; i < rt.TypeArguments.Count; ++i) {
if (i > 0) builder.Append(", ");
builder.Append(Convert(rt.TypeParameters[i]));
builder.Append(Convert(rt.TypeArguments[i]));
}
builder.Append('>');
}

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

@ -127,7 +127,7 @@ namespace CSharpBinding @@ -127,7 +127,7 @@ namespace CSharpBinding
if (methods.Count == 0) return;
bool overloadIsSure;
if (methods.Count == 1) {
overloadIsSure = true;
overloadIsSure = true;
dp.DefaultIndex = 0;
} else {
IReturnType[] parameterTypes = new IReturnType[paramCount + 1];
@ -194,7 +194,7 @@ namespace CSharpBinding @@ -194,7 +194,7 @@ namespace CSharpBinding
bool IsInComment(SharpDevelopTextAreaControl editor)
{
Parser.ExpressionFinder ef = new Parser.ExpressionFinder(editor.FileName);
int cursor = editor.ActiveTextAreaControl.Caret.Offset;
int cursor = editor.ActiveTextAreaControl.Caret.Offset - 1;
return ef.FilterComments(editor.Document.GetText(0, cursor + 1), ref cursor) == null;
}

4
src/AddIns/BackendBindings/VBNetBinding/Project/Src/VBNetAmbience.cs

@ -505,9 +505,9 @@ namespace VBNetBinding @@ -505,9 +505,9 @@ namespace VBNetBinding
ConstructedReturnType rt = (ConstructedReturnType)returnType;
UnpackNestedType(builder, rt.BaseType);
builder.Append("(Of ");
for (int i = 0; i < rt.TypeParameters.Count; ++i) {
for (int i = 0; i < rt.TypeArguments.Count; ++i) {
if (i > 0) builder.Append(", ");
builder.Append(Convert(rt.TypeParameters[i]));
builder.Append(Convert(rt.TypeArguments[i]));
}
builder.Append(')');
}

10
src/Main/Base/Project/Src/Dom/Implementations/ConstructedReturnType.cs

@ -27,7 +27,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -27,7 +27,7 @@ namespace ICSharpCode.SharpDevelop.Dom
List<IReturnType> typeParameters;
IReturnType baseType;
public List<IReturnType> TypeParameters {
public List<IReturnType> TypeArguments {
get {
return typeParameters;
}
@ -78,7 +78,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -78,7 +78,7 @@ namespace ICSharpCode.SharpDevelop.Dom
} else if (t is ArrayReturnType) {
return CheckReturnType(((ArrayReturnType)t).ElementType);
} else if (t is ConstructedReturnType) {
foreach (IReturnType para in ((ConstructedReturnType)t).TypeParameters) {
foreach (IReturnType para in ((ConstructedReturnType)t).TypeArguments) {
if (CheckReturnType(para)) return true;
}
return false;
@ -136,9 +136,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -136,9 +136,9 @@ namespace ICSharpCode.SharpDevelop.Dom
return new ArrayReturnType(t, input.ArrayDimensions);
} else if (input is ConstructedReturnType) {
ConstructedReturnType r = (ConstructedReturnType)input;
List<IReturnType> para = new List<IReturnType>(r.TypeParameters.Count);
for (int i = 0; i < r.TypeParameters.Count; ++i) {
para.Add(TranslateType(r.TypeParameters[i], typeParameters, convertForMethod));
List<IReturnType> para = new List<IReturnType>(r.TypeArguments.Count);
for (int i = 0; i < r.TypeArguments.Count; ++i) {
para.Add(TranslateType(r.TypeArguments[i], typeParameters, convertForMethod));
}
return new ConstructedReturnType(r.baseType, para);
}

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

@ -155,6 +155,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -155,6 +155,7 @@ namespace ICSharpCode.SharpDevelop.Dom
out bool acceptableMatch,
out IReturnType[][] inferredTypeParameters)
{
// § 14.4.2 Overload resolution
acceptableMatch = false;
inferredTypeParameters = null;
if (list.Count == 0) return new int[] {};
@ -163,9 +164,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -163,9 +164,9 @@ namespace ICSharpCode.SharpDevelop.Dom
bool[] needToExpand = new bool[list.Count];
int maxScore = 0;
int baseScore = 0;
int score;
bool expanded;
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;
@ -183,8 +184,41 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -183,8 +184,41 @@ namespace ICSharpCode.SharpDevelop.Dom
// The first step is to expand the methods and do type argument substitution
IReturnType[][] expandedParameters = ExpandParametersAndSubstitute(list, arguments, maxScore, ranking, needToExpand, out inferredTypeParameters);
// § 14.4.2.2 Better function member
// find the best function member
score = baseScore + 2;
int bestIndex = -1;
for (int i = 0; i < ranking.Length; i++) {
if (ranking[i] == maxScore) {
// the best function member is the one that is better than all other function members
if (bestIndex < 0) {
ranking[i] = score;
bestIndex = i;
} else {
switch (GetBetterFunctionMember(arguments,
list[i], expandedParameters[i], needToExpand[i],
list[bestIndex], expandedParameters[bestIndex], needToExpand[bestIndex]))
{
case 0:
// neither member is better
ranking[i] = score;
break;
case 1:
// the new member is better
ranking[i] = ++score;
bestIndex = i;
break;
case 2:
// the old member is better
ranking[i] = score - 1;
// this is not really correct, normally we would need to
// compare the member with other members
break;
}
}
}
}
return ranking;
}
@ -224,9 +258,104 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -224,9 +258,104 @@ namespace ICSharpCode.SharpDevelop.Dom
return expandedParameters;
}
static bool IsBetterFunctionMember()
/// <summary>
/// Gets which function member is better. (§ 14.4.2.2)
/// </summary>
/// <param name="arguments">The arguments passed to the function</param>
/// <param name="m1">The first method</param>
/// <param name="parameters1">The expanded and substituted parameters of the first method</param>
/// <param name="m2">The second method</param>
/// <param name="parameters2">The expanded and substituted parameters of the second method</param>
/// <returns>0 if neither method is better. 1 if m1 is better. 2 if m2 is better.</returns>
static int GetBetterFunctionMember(IReturnType[] arguments,
IMethodOrProperty m1, IReturnType[] parameters1, bool isExpanded1,
IMethodOrProperty m2, IReturnType[] parameters2, bool isExpanded2)
{
return false;
int length = Math.Min(Math.Min(parameters1.Length, parameters2.Length), arguments.Length);
bool foundBetterParamIn1 = false;
bool foundBetterParamIn2 = false;
for (int i = 0; i < length; i++) {
if (arguments[i] == null)
continue;
int res = GetBetterConversion(arguments[i], parameters1[i], parameters2[i]);
if (res == 1) foundBetterParamIn1 = true;
if (res == 2) foundBetterParamIn2 = true;
}
if (foundBetterParamIn1 && !foundBetterParamIn2)
return 1;
if (foundBetterParamIn2 && !foundBetterParamIn1)
return 2;
if (foundBetterParamIn1 && foundBetterParamIn2)
return 0; // ambigous
// If none conversion is better than any other, it is possible that the
// expanded parameter lists are the same:
for (int i = 0; i < length; i++) {
if (!object.Equals(parameters1[i], parameters2[i])) {
// if expanded parameters are not the same, neither function member is better
return 0;
}
}
// the expanded parameters are the same, apply the tie-breaking rules from the spec:
// if one method is generic and the other non-generic, the non-generic is better
bool m1IsGeneric = (m1 is IMethod) ? ((IMethod)m1).TypeParameters.Count > 0 : false;
bool m2IsGeneric = (m2 is IMethod) ? ((IMethod)m2).TypeParameters.Count > 0 : false;
if (m1IsGeneric && !m2IsGeneric) return 2;
if (m2IsGeneric && !m1IsGeneric) return 1;
// for params parameters: non-expanded calls are better
if (isExpanded1 && !isExpanded2) return 2;
if (isExpanded2 && !isExpanded1) return 1;
// if the number of parameters is different, the one with more parameters is better
// this occurs when only when both methods are expanded
if (m1.Parameters.Count > m2.Parameters.Count) return 1;
if (m2.Parameters.Count > m1.Parameters.Count) return 2;
IReturnType[] m1ParamTypes = new IReturnType[m1.Parameters.Count];
IReturnType[] m2ParamTypes = new IReturnType[m2.Parameters.Count];
for (int i = 0; i < m1ParamTypes.Length; i++) {
m1ParamTypes[i] = m1.Parameters[i].ReturnType;
m2ParamTypes[i] = m2.Parameters[i].ReturnType;
}
return GetMoreSpecific(m1ParamTypes, m2ParamTypes);
}
/// <summary>
/// Gets which return type list is more specific.
/// § 14.4.2.2: types with generic arguments are less specific than types with fixed arguments
/// </summary>
/// <returns>0 if both are equally specific, 1 if <paramref name="r"/> is more specific,
/// 2 if <paramref name="s"/> is more specific.</returns>
static int GetMoreSpecific(IList<IReturnType> r, IList<IReturnType> s)
{
bool foundMoreSpecificParamIn1 = false;
bool foundMoreSpecificParamIn2 = false;
int length = Math.Min(r.Count, s.Count);
for (int i = 0; i < length; i++) {
int res = GetMoreSpecific(r[i], s[i]);
if (res == 1) foundMoreSpecificParamIn1 = true;
if (res == 2) foundMoreSpecificParamIn2 = true;
}
if (foundMoreSpecificParamIn1 && !foundMoreSpecificParamIn2)
return 1;
if (foundMoreSpecificParamIn2 && !foundMoreSpecificParamIn1)
return 2;
return 0;
}
static int GetMoreSpecific(IReturnType r, IReturnType s)
{
if (r is GenericReturnType && !(s is GenericReturnType))
return 2;
if (s is GenericReturnType && !(r is GenericReturnType))
return 1;
if (r is ArrayReturnType && s is ArrayReturnType)
return GetMoreSpecific(((ArrayReturnType)r).ElementType, ((ArrayReturnType)s).ElementType);
if (r is ConstructedReturnType && s is ConstructedReturnType)
return GetMoreSpecific(((ConstructedReturnType)r).TypeArguments, ((ConstructedReturnType)s).TypeArguments);
return 0;
}
#endregion
@ -272,7 +401,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -272,7 +401,7 @@ namespace ICSharpCode.SharpDevelop.Dom
case "System.Collections.Generic.IList":
case "System.Collections.Generic.ICollection":
case "System.Collections.Generic.IEnumerable":
return InferTypeArgument(ea.ElementType, ((ConstructedReturnType)passedArgument).TypeParameters[0], outputArray);
return InferTypeArgument(ea.ElementType, ((ConstructedReturnType)passedArgument).TypeArguments[0], outputArray);
}
}
// If P is an array type, and A is not an array type of the same rank,
@ -293,9 +422,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -293,9 +422,9 @@ namespace ICSharpCode.SharpDevelop.Dom
// For our purposes, we can simplify enourmously:
ConstructedReturnType passed = passedArgument as ConstructedReturnType;
if (passed == null) return false;
int count = Math.Min(constructed.TypeParameters.Count, passed.TypeParameters.Count);
int count = Math.Min(constructed.TypeArguments.Count, passed.TypeArguments.Count);
for (int i = 0; i < count; i++) {
InferTypeArgument(constructed.TypeParameters[i], passed.TypeParameters[i], outputArray);
InferTypeArgument(constructed.TypeArguments[i], passed.TypeArguments[i], outputArray);
}
}
return true;

8
src/Main/Base/Project/Src/Gui/Components/StatusBar/SdStatusBar.cs

@ -123,7 +123,9 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -123,7 +123,9 @@ namespace ICSharpCode.SharpDevelop.Gui
{
taskName = name;
this.totalWork = totalWork;
this.BeginInvoke(new MethodInvoker(MakeVisible));
if (this.IsHandleCreated) {
this.BeginInvoke(new MethodInvoker(MakeVisible));
}
}
void MakeVisible()
@ -165,7 +167,9 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -165,7 +167,9 @@ namespace ICSharpCode.SharpDevelop.Gui
public void Done()
{
taskName = null;
this.BeginInvoke(new MethodInvoker(MakeInvisible));
if (this.IsHandleCreated) {
this.BeginInvoke(new MethodInvoker(MakeInvisible));
}
}
string taskName;

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

@ -70,7 +70,7 @@ class TestClass { @@ -70,7 +70,7 @@ class TestClass {
Assert.AreEqual(1, m.Parameters.Count);
Assert.IsTrue(m.Parameters[0].ReturnType is ConstructedReturnType);
Assert.AreEqual("System.Collections.Generic.IEnumerable", m.Parameters[0].ReturnType.FullyQualifiedName);
Assert.AreEqual("TestClass", ((ConstructedReturnType)m.Parameters[0].ReturnType).TypeParameters[0].FullyQualifiedName);
Assert.AreEqual("TestClass", ((ConstructedReturnType)m.Parameters[0].ReturnType).TypeArguments[0].FullyQualifiedName);
}
[Test]
@ -92,7 +92,7 @@ class TestClass { @@ -92,7 +92,7 @@ class TestClass {
Assert.IsTrue(result is TypeResolveResult);
Assert.AreEqual("System.Collections.Generic.List", ((TypeResolveResult)result).ResolvedClass.FullyQualifiedName);
Assert.IsTrue(result.ResolvedType is ConstructedReturnType);
Assert.AreEqual("System.String", ((ConstructedReturnType)result.ResolvedType).TypeParameters[0].FullyQualifiedName);
Assert.AreEqual("System.String", ((ConstructedReturnType)result.ResolvedType).TypeArguments[0].FullyQualifiedName);
}
[Test]

1
src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj

@ -49,6 +49,7 @@ @@ -49,6 +49,7 @@
<Compile Include="InnerClassesResolverTests.cs" />
<Compile Include="RefactoringTests.cs" />
<Compile Include="SearchClassTests.cs" />
<Compile Include="OverloadFinding.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Project\ICSharpCode.SharpDevelop.csproj">

14
src/Main/Base/Test/NRefactoryResolverTests.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"/>
@ -25,7 +25,8 @@ namespace ICSharpCode.SharpDevelop.Tests @@ -25,7 +25,8 @@ namespace ICSharpCode.SharpDevelop.Tests
p.ParseMethodBodies = false;
p.Parse();
DefaultProjectContent pc = new DefaultProjectContent();
pc.ReferencedContents.Add(corLib);
pc.ReferencedContents.Add(ProjectContentRegistry.Mscorlib);
pc.ReferencedContents.Add(ProjectContentRegistry.WinForms);
ParserService.ForceProjectContent(pc);
lastPC = pc;
NRefactoryASTConvertVisitor visitor = new NRefactoryASTConvertVisitor(pc);
@ -48,7 +49,8 @@ namespace ICSharpCode.SharpDevelop.Tests @@ -48,7 +49,8 @@ namespace ICSharpCode.SharpDevelop.Tests
p.Parse();
DefaultProjectContent pc = new DefaultProjectContent();
ParserService.ForceProjectContent(pc);
pc.ReferencedContents.Add(corLib);
pc.ReferencedContents.Add(ProjectContentRegistry.Mscorlib);
pc.ReferencedContents.Add(ProjectContentRegistry.WinForms);
pc.Language = LanguageProperties.VBNet;
lastPC = pc;
NRefactoryASTConvertVisitor visitor = new NRefactoryASTConvertVisitor(pc);
@ -92,7 +94,7 @@ namespace ICSharpCode.SharpDevelop.Tests @@ -92,7 +94,7 @@ namespace ICSharpCode.SharpDevelop.Tests
public T Resolve<T>(string program, string expression, int line) where T : ResolveResult
{
ResolveResult rr = Resolve(program, expression, line);
Assert.IsNotNull(rr);
Assert.IsNotNull(rr, "Resolve returned null");
Assert.IsTrue(rr is T, "result is " + typeof(T).Name);
return (T)rr;
}
@ -100,12 +102,10 @@ namespace ICSharpCode.SharpDevelop.Tests @@ -100,12 +102,10 @@ namespace ICSharpCode.SharpDevelop.Tests
public T ResolveVB<T>(string program, string expression, int line) where T : ResolveResult
{
ResolveResult rr = ResolveVB(program, expression, line);
Assert.IsNotNull(rr);
Assert.IsNotNull(rr, "Resolve returned null");
Assert.IsTrue(rr is T, "result is " + typeof(T).Name);
return (T)rr;
}
IProjectContent corLib = ProjectContentRegistry.Mscorlib;
#endregion
#region Test for old issues (Fidalgo)

81
src/Main/Base/Test/OverloadFinding.cs

@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
// <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.Text;
using NUnit.Framework;
using ICSharpCode.SharpDevelop.Dom;
namespace ICSharpCode.SharpDevelop.Tests
{
[TestFixture]
public class OverloadFinding
{
[Test] public void Simple()
{
Test("(\"Hallo\")", 0, "(string a)", "(int b)");
Test("(2)", 1, "(string a)", "(int b)");
}
[Test] public void WinForms()
{
string[] overloads = {"(object a)", "(TextBoxBase a)", "(Control a)", "(RichTextBox a)"};
Test("(new RichTextBox())", 3, overloads);
Test("(new Control())", 2, overloads);
Test("(new TextBox())", 1, overloads);
Test("(new Button())", 2, overloads);
Test("(3)", 0, overloads);
}
[Test] public void Params()
{
string[] overloads = {"(params int[] a)", "(int a, params int[] b)"};
Test("()", 0, overloads);
Test("(1)", 1, overloads);
Test("(1, 2)", 1, overloads);
}
[Test] public void IntegerConversion()
{
string[] overloads = {"<T>(T a)", "(int a)"};
Test("(1)", 1, overloads);
Test("(short.MaxValue)", 1, overloads);
Test("(long.MaxValue)", 0, overloads);
}
NRefactoryResolverTests nrrt = new NRefactoryResolverTests();
void Test(string callExpr, int num, params string[] signatures)
{
StringBuilder b = new StringBuilder();
int lineNumber = 0;
++lineNumber; b.AppendLine("using System;");
++lineNumber; b.AppendLine("using System.Windows.Forms;");
++lineNumber; b.AppendLine("class TestClass {");
++lineNumber; b.AppendLine(" void callingMethod() {");
++lineNumber; b.AppendLine(" ");
int callPosition = lineNumber;
++lineNumber; b.AppendLine(" }");
int[] positions = new int[signatures.Length];
for (int i = 0; i < signatures.Length; i++) {
b.Append(" void Method");
b.Append(signatures[i]);
++lineNumber; b.AppendLine(" {");
positions[i] = lineNumber;
++lineNumber; b.AppendLine(" }");
}
b.AppendLine("}");
MemberResolveResult mrr = nrrt.Resolve<MemberResolveResult>(b.ToString(), "Method" + callExpr, callPosition);
string msg = "wrong overload: ";
for (int i = 0; i < positions.Length; i++) {
if (positions[i] == mrr.ResolvedMember.Region.BeginLine)
msg += signatures[i];
}
Assert.AreEqual(positions[num], mrr.ResolvedMember.Region.BeginLine, msg);
}
}
}
Loading…
Cancel
Save