Browse Source

Rewrote type inference and overload resolution. The new version has support for implicitly typed lambda expressions.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@3009 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 18 years ago
parent
commit
420b068acd
  1. 7
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs
  2. 646
      src/Libraries/NRefactory/Project/Src/Parser/CSharp/Parser.cs
  3. 17
      src/Libraries/NRefactory/Project/Src/Parser/CSharp/cs.ATG
  4. 30
      src/Libraries/NRefactory/Project/Src/Visitors/LookupTableVisitor.cs
  5. 23
      src/Main/Base/Project/Src/TextEditor/Gui/Editor/NRefactoryCodeCompletionBinding.cs
  6. 119
      src/Main/Base/Test/GenericResolverTests.cs
  7. 26
      src/Main/Base/Test/MemberLookupHelperTests.cs
  8. 47
      src/Main/Base/Test/NRefactoryResolverTests.cs
  9. 15
      src/Main/Base/Test/OverloadFinding.cs
  10. 5
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj
  11. 330
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/OverloadResolution.cs
  12. 480
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/TypeInference.cs
  13. 5
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs
  14. 34
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AnonymousMethodReturnType.cs
  15. 2
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ArrayReturnType.cs
  16. 10
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs
  17. 4
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultTypeParameter.cs
  18. 49
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/MethodGroupReturnType.cs
  19. 35
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ProxyReturnType.cs
  20. 474
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs
  21. 69
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaParameterReturnType.cs
  22. 100
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaReturnType.cs
  23. 91
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs
  24. 31
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs
  25. 2
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/VBNetToCSharpConvertVisitor.cs
  26. 16
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs

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

@ -94,10 +94,9 @@ namespace CSharpBinding @@ -94,10 +94,9 @@ namespace CSharpBinding
}
}
if ((char.IsLetter(ch) || ch == '_') && CodeCompletionOptions.CompleteWhenTyping) {
if (cursor > 1 && !char.IsLetterOrDigit(editor.Document.GetCharAt(cursor - 1))
&& !IsInComment(editor))
{
if (char.IsLetter(ch) && CodeCompletionOptions.CompleteWhenTyping) {
char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' ';
if (!char.IsLetterOrDigit(prevChar) && prevChar != '.' && !IsInComment(editor)) {
ExpressionResult result = ef.FindExpression(editor.Text, cursor);
LoggingService.Debug("CC: Beginning to type a word, result=" + result);
if (result.Context != ExpressionContext.IdentifierExpected) {

646
src/Libraries/NRefactory/Project/Src/Parser/CSharp/Parser.cs

File diff suppressed because it is too large Load Diff

17
src/Libraries/NRefactory/Project/Src/Parser/CSharp/cs.ATG

@ -1900,19 +1900,22 @@ PrimaryExpr<out Expression pexpr> @@ -1900,19 +1900,22 @@ PrimaryExpr<out Expression pexpr>
| MemberAccess<out pexpr, pexpr>
/*--- invocation expression: */
| "(" (. List<Expression> parameters = new List<Expression>(); .)
[ Argument<out expr> (. if (expr != null) {parameters.Add(expr);} .)
{ "," Argument<out expr> (. if (expr != null) {parameters.Add(expr);} .)
| "("
(. List<Expression> parameters = new List<Expression>(); .)
(. pexpr = new InvocationExpression(pexpr, parameters); .)
[ Argument<out expr> (. SafeAdd(pexpr, parameters, expr); .)
{ "," Argument<out expr> (. SafeAdd(pexpr, parameters, expr); .)
}
]
")" (. pexpr = new InvocationExpression(pexpr, parameters); .)
")"
/*--- element access */
| (. /*if (isArrayCreation) Error("element access not allow on array creation");*/
List<Expression> indices = new List<Expression>();
pexpr = new IndexerExpression(pexpr, indices);
.)
"[" Expr<out expr> (. if (expr != null) { indices.Add(expr); } .)
{ "," Expr<out expr> (. if (expr != null) { indices.Add(expr); } .)
} "]" (. pexpr = new IndexerExpression(pexpr, indices); .)
"[" Expr<out expr> (. SafeAdd(pexpr, indices, expr); .)
{ "," Expr<out expr> (. SafeAdd(pexpr, indices, expr); .)
} "]"
(. if (pexpr != null) {
pexpr.StartLocation = startLocation;

30
src/Libraries/NRefactory/Project/Src/Visitors/LookupTableVisitor.cs

@ -21,8 +21,9 @@ namespace ICSharpCode.NRefactory.Visitors @@ -21,8 +21,9 @@ namespace ICSharpCode.NRefactory.Visitors
public readonly bool IsConst;
public readonly bool IsLoopVariable;
public readonly Expression Initializer;
public readonly LambdaExpression ParentLambdaExpression;
public LocalLookupVariable(string name, TypeReference typeRef, Location startPos, Location endPos, bool isConst, bool isLoopVariable, Expression initializer)
public LocalLookupVariable(string name, TypeReference typeRef, Location startPos, Location endPos, bool isConst, bool isLoopVariable, Expression initializer, LambdaExpression parentLambdaExpression)
{
this.Name = name;
this.TypeRef = typeRef;
@ -31,6 +32,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -31,6 +32,7 @@ namespace ICSharpCode.NRefactory.Visitors
this.IsConst = isConst;
this.IsLoopVariable = isLoopVariable;
this.Initializer = initializer;
this.ParentLambdaExpression = parentLambdaExpression;
}
}
@ -66,7 +68,8 @@ namespace ICSharpCode.NRefactory.Visitors @@ -66,7 +68,8 @@ namespace ICSharpCode.NRefactory.Visitors
public void AddVariable(TypeReference typeRef, string name,
Location startPos, Location endPos, bool isConst,
bool isLoopVariable, Expression initializer)
bool isLoopVariable, Expression initializer,
LambdaExpression parentLambdaExpression)
{
if (name == null || name.Length == 0) {
return;
@ -77,7 +80,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -77,7 +80,7 @@ namespace ICSharpCode.NRefactory.Visitors
} else {
list = (List<LocalLookupVariable>)variables[name];
}
list.Add(new LocalLookupVariable(name, typeRef, startPos, endPos, isConst, isLoopVariable, initializer));
list.Add(new LocalLookupVariable(name, typeRef, startPos, endPos, isConst, isLoopVariable, initializer, parentLambdaExpression));
}
public override object VisitWithStatement(WithStatement withStatement, object data)
@ -112,7 +115,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -112,7 +115,7 @@ namespace ICSharpCode.NRefactory.Visitors
localVariableDeclaration.StartLocation,
CurrentEndLocation,
(localVariableDeclaration.Modifier & Modifiers.Const) == Modifiers.Const,
false, varDecl.Initializer);
false, varDecl.Initializer, null);
}
return base.VisitLocalVariableDeclaration(localVariableDeclaration, data);
}
@ -122,7 +125,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -122,7 +125,7 @@ namespace ICSharpCode.NRefactory.Visitors
foreach (ParameterDeclarationExpression p in anonymousMethodExpression.Parameters) {
AddVariable(p.TypeReference, p.ParameterName,
anonymousMethodExpression.StartLocation, anonymousMethodExpression.EndLocation,
false, false, null);
false, false, null, null);
}
return base.VisitAnonymousMethodExpression(anonymousMethodExpression, data);
}
@ -132,7 +135,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -132,7 +135,7 @@ namespace ICSharpCode.NRefactory.Visitors
foreach (ParameterDeclarationExpression p in lambdaExpression.Parameters) {
AddVariable(p.TypeReference, p.ParameterName,
lambdaExpression.StartLocation, lambdaExpression.EndLocation,
false, false, null);
false, false, null, lambdaExpression);
}
return base.VisitLambdaExpression(lambdaExpression, data);
}
@ -143,7 +146,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -143,7 +146,7 @@ namespace ICSharpCode.NRefactory.Visitors
if (parentExpression != null) {
AddVariable(fromClause.Type, fromClause.Identifier,
parentExpression.StartLocation, parentExpression.EndLocation,
false, true, fromClause.InExpression);
false, true, fromClause.InExpression, null);
}
return base.VisitQueryExpressionFromClause(fromClause, data);
}
@ -155,18 +158,18 @@ namespace ICSharpCode.NRefactory.Visitors @@ -155,18 +158,18 @@ namespace ICSharpCode.NRefactory.Visitors
if (parentExpression != null) {
AddVariable(joinClause.Type, joinClause.Identifier,
parentExpression.StartLocation, parentExpression.EndLocation,
false, true, joinClause.InExpression);
false, true, joinClause.InExpression, null);
}
} else {
AddVariable(joinClause.Type, joinClause.Identifier,
joinClause.StartLocation, joinClause.EndLocation,
false, true, joinClause.InExpression);
false, true, joinClause.InExpression, null);
QueryExpression parentExpression = joinClause.Parent as QueryExpression;
if (parentExpression != null) {
AddVariable(joinClause.Type, joinClause.IntoIdentifier,
parentExpression.StartLocation, parentExpression.EndLocation,
false, false, joinClause.InExpression);
false, false, joinClause.InExpression, null);
}
}
return base.VisitQueryExpressionJoinClause(joinClause, data);
@ -178,7 +181,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -178,7 +181,7 @@ namespace ICSharpCode.NRefactory.Visitors
if (parentExpression != null) {
AddVariable(null, letClause.Identifier,
parentExpression.StartLocation, parentExpression.EndLocation,
false, false, letClause.Expression);
false, false, letClause.Expression, null);
}
return base.VisitQueryExpressionLetClause(letClause, data);
}
@ -238,7 +241,8 @@ namespace ICSharpCode.NRefactory.Visitors @@ -238,7 +241,8 @@ namespace ICSharpCode.NRefactory.Visitors
foreachStatement.StartLocation,
foreachStatement.EndLocation,
false, true,
foreachStatement.Expression);
foreachStatement.Expression,
null);
if (foreachStatement.Expression != null) {
foreachStatement.Expression.AcceptVisitor(this, data);
@ -265,7 +269,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -265,7 +269,7 @@ namespace ICSharpCode.NRefactory.Visitors
catchClause.VariableName,
catchClause.StatementBlock.StartLocation,
catchClause.StatementBlock.EndLocation,
false, false, null);
false, false, null, null);
}
catchClause.StatementBlock.AcceptVisitor(this, data);
}

23
src/Main/Base/Project/Src/TextEditor/Gui/Editor/NRefactoryCodeCompletionBinding.cs

@ -215,30 +215,17 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor @@ -215,30 +215,17 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
overloadIsSure = true;
dp.DefaultIndex = 0;
} else {
IReturnType[] parameterTypes = new IReturnType[paramCount + 1];
IReturnType[] argumentTypes = new IReturnType[paramCount + 1];
int i = 0;
foreach (ResolveResult rr in parameters) {
if (rr != null) {
parameterTypes[i] = rr.ResolvedType;
argumentTypes[i] = rr.ResolvedType;
}
i++;
}
IReturnType[][] tmp;
int[] ranking = MemberLookupHelper.RankOverloads(methods, parameterTypes, true, out overloadIsSure, out tmp);
bool multipleBest = false;
int bestRanking = -1;
int best = 0;
for (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;
IMethodOrProperty result = Dom.CSharp.OverloadResolution.FindOverload(
methods, argumentTypes, true, false, out overloadIsSure);
dp.DefaultIndex = methods.IndexOf(result);
}
editor.ShowInsightWindow(dp);
if (overloadIsSure) {

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

@ -417,5 +417,124 @@ class D : Program { @@ -417,5 +417,124 @@ class D : Program {
Assert.AreEqual("System.Object.Equals", mrr.ResolvedMember.FullyQualifiedName);
}
#endregion
#region C# 3.0 Type Inference
MemberResolveResult ResolveInSelectProgram(string expression)
{
string program = @"using System;
using System.Collections.Generic;
delegate R Func<A, R>(A arg);
static class TestClass {
static void Main() {
{XXX};
}
static IEnumerable<TResult> Select<TSource,TResult>(
this IEnumerable<TSource> source, Func<TSource,TResult> selector)
{
foreach (TSource element in source) yield return selector(element);
}
static double StringToDouble(string s) { return double.Parse(s); }
}
";
return Resolve<MemberResolveResult>(program.Replace("{XXX}", expression), expression, 6, 3);
}
[Test]
public void SelectWithExplicitDelegate()
{
MemberResolveResult mrr = ResolveInSelectProgram("Select(new string[0], new Func<string, double>(StringToDouble))");
Assert.AreEqual("System.Collections.Generic.IEnumerable{System.Double}", mrr.ResolvedType.DotNetName);
}
[Test]
public void SelectWithArgumentIgnoringAnonymousMethod()
{
MemberResolveResult mrr = ResolveInSelectProgram("Select(new string[0], delegate { return DateTime.MinValue; })");
Assert.AreEqual("System.Collections.Generic.IEnumerable{System.DateTime}", mrr.ResolvedType.DotNetName);
}
[Test]
public void SelectWithExplicitlyTypedLambda()
{
MemberResolveResult mrr = ResolveInSelectProgram("Select(new string[0], (string s) => s.Length)");
Assert.AreEqual("System.Collections.Generic.IEnumerable{System.Int32}", mrr.ResolvedType.DotNetName);
}
[Test]
public void SelectWithImplicitlyTypedLambda()
{
MemberResolveResult mrr = ResolveInSelectProgram("Select(new string[0], s => s.Length)");
Assert.AreEqual("System.Collections.Generic.IEnumerable{System.Int32}", mrr.ResolvedType.DotNetName);
}
[Test]
public void SelectWithImplicitlyTypedLambdaCalledAsExtensionMethod()
{
MemberResolveResult mrr = ResolveInSelectProgram("(new string[0]).Select(s => s.Length)");
Assert.AreEqual("System.Collections.Generic.IEnumerable{System.Int32}", mrr.ResolvedType.DotNetName);
}
[Test]
public void MultipleOverloadsWithDifferentParameterCounts()
{
string program = @"class MainClass {
void Main() {
M(x=>x.ToUpper());
}
delegate R Func<T, R>(T arg);
T M<T>(Func<string, T> f){ /* whatever ... */ }
T M<T>(Func<string, T> f, T g){ /* whatever ... */ }
}";
var mrr = nrrt.Resolve<MemberResolveResult>(program, "M(x=>x.ToUpper())", 3, 3, ExpressionContext.Default);
Assert.AreEqual("System.String", mrr.ResolvedType.DotNetName);
Assert.AreEqual(1, ((IMethod)mrr.ResolvedMember).Parameters.Count);
}
[Test]
public void MultipleOverloadsWithDifferentParameterCountsAsExtensionMethod()
{
string program = @"class MainClass {
static void Main() {
(string.Empty).M(x=>x.ToUpper());
}
delegate R Func<T, R>(T arg);
static T M<X, T>(this X x, Func<X, T> f){ /* whatever ... */ }
static T M<X, T>(this X x, Func<X, T> f, T g){ /* whatever ... */ }
}";
var mrr = nrrt.Resolve<MemberResolveResult>(program, "(string.Empty).M(x=>x.ToUpper())", 3, 3, ExpressionContext.Default);
Assert.AreEqual("System.String", mrr.ResolvedType.DotNetName);
Assert.AreEqual(2, ((IMethod)mrr.ResolvedMember).Parameters.Count);
}
[Test]
public void EnsureApplicabilityIsTestedAfterTypeInference()
{
string program = @"class TestClass {
static void Main() {
}
static void G(object obj1, object obj2) {}
static void G<T>(T a, T b) {}
}
";
var mrr = Resolve<MemberResolveResult>(program, "G(1, 2)", 3);
Assert.AreEqual("TestClass.G<T>(int a, int b)", ToCSharp(mrr.ResolvedMember));
mrr = Resolve<MemberResolveResult>(program, "G(1, 2.2)", 3);
Assert.AreEqual("TestClass.G<T>(double a, double b)", ToCSharp(mrr.ResolvedMember));
mrr = Resolve<MemberResolveResult>(program, "G(1, \"a\")", 3);
Assert.AreEqual("TestClass.G(object obj1, object obj2)", ToCSharp(mrr.ResolvedMember));
}
string ToCSharp(IEntity entity)
{
return (new Dom.CSharp.CSharpAmbience {
ConversionFlags = ConversionFlags.UseFullyQualifiedMemberNames
| ConversionFlags.ShowParameterList | ConversionFlags.ShowParameterNames
| ConversionFlags.ShowTypeParameterList
}).Convert(entity);
}
}
#endregion
}

26
src/Main/Base/Test/MemberLookupHelperTests.cs

@ -311,14 +311,14 @@ namespace ICSharpCode.SharpDevelop.Tests @@ -311,14 +311,14 @@ namespace ICSharpCode.SharpDevelop.Tests
// but it is applicable
Assert.IsTrue(IsApplicable(msc.SystemTypes.String,
CreateT()));
CreateT()));
}
[Test]
public void NoConversionExistsFromStringToDisposableT()
{
Assert.IsFalse(IsApplicable(msc.SystemTypes.String,
CreateTWithDisposableConstraint()));
CreateTWithDisposableConstraint()));
Assert.IsFalse(MemberLookupHelper.ConversionExists(msc.SystemTypes.String,
CreateTWithDisposableConstraint()));
@ -372,7 +372,7 @@ namespace ICSharpCode.SharpDevelop.Tests @@ -372,7 +372,7 @@ namespace ICSharpCode.SharpDevelop.Tests
public void ConversionExistsFromAnonymousDelegateToSystemPredicate()
{
Assert.IsTrue(IsApplicable(
new AnonymousMethodReturnType(new DefaultCompilationUnit(msc)),
new AnonymousMethodReturnType(new DefaultCompilationUnit(msc)) { MethodReturnType = msc.SystemTypes.Boolean },
new GetClassReturnType(msc, "System.Predicate", 1)
));
}
@ -392,11 +392,27 @@ namespace ICSharpCode.SharpDevelop.Tests @@ -392,11 +392,27 @@ namespace ICSharpCode.SharpDevelop.Tests
public void ConversionExistsFromAnonymousDelegateWithParameterToSystemPredicate()
{
AnonymousMethodReturnType amrt = new AnonymousMethodReturnType(new DefaultCompilationUnit(msc));
amrt.MethodReturnType = msc.SystemTypes.Boolean;
amrt.MethodParameters = new List<IParameter>();
amrt.MethodParameters.Add(new DefaultParameter("test", msc.SystemTypes.Object, DomRegion.Empty));
amrt.MethodParameters.Add(new DefaultParameter("test", msc.SystemTypes.String, DomRegion.Empty));
Assert.IsTrue(MemberLookupHelper.ConversionExists(
amrt,
new GetClassReturnType(msc, "System.Predicate", 1)
new ConstructedReturnType(new GetClassReturnType(msc, "System.Predicate", 1),
new IReturnType[] { msc.SystemTypes.String })
));
}
[Test]
public void ConversionDoesNotExistFromAnonymousDelegateWithParameterToSystemPredicateWhenParameterTypeIsIncompatible()
{
AnonymousMethodReturnType amrt = new AnonymousMethodReturnType(new DefaultCompilationUnit(msc));
amrt.MethodReturnType = msc.SystemTypes.Boolean;
amrt.MethodParameters = new List<IParameter>();
amrt.MethodParameters.Add(new DefaultParameter("test", msc.SystemTypes.String, DomRegion.Empty));
Assert.IsFalse(MemberLookupHelper.ConversionExists(
amrt,
new ConstructedReturnType(new GetClassReturnType(msc, "System.Predicate", 1),
new IReturnType[] { msc.SystemTypes.Int32 })
));
}
}

47
src/Main/Base/Test/NRefactoryResolverTests.cs

@ -2074,5 +2074,52 @@ class DerivedClass : MiddleClass { @@ -2074,5 +2074,52 @@ class DerivedClass : MiddleClass {
MemberResolveResult mrr = Resolve<MemberResolveResult>(program, "new DerivedClass().Test(3);", 4);
Assert.AreEqual("MiddleClass.Test", (mrr.ResolvedMember).FullyQualifiedName);
}
[Test]
public void SimpleLambdaTest()
{
string program = @"using System;
class TestClass {
static void Main() {
Test(i => Console.WriteLine(i));
}
public void Test(Action<int> ac) { ac(42); }
}";
var lrr = Resolve<LocalResolveResult>(program, "i", 4, 31, ExpressionContext.Default);
Assert.AreEqual("System.Int32", lrr.ResolvedType.DotNetName);
lrr = Resolve<LocalResolveResult>(program, "i", 4, 8, ExpressionContext.Default);
Assert.AreEqual("System.Int32", lrr.ResolvedType.DotNetName);
}
[Test]
public void IncompleteLambdaTest()
{
string program = @"using System;
class TestClass {
static void Main() {
Test(i => i
}
public void Test(Action<int> ac) { ac(42); }
}";
var lrr = Resolve<LocalResolveResult>(program, "i", 4, 13, ExpressionContext.Default);
Assert.AreEqual("System.Int32", lrr.ResolvedType.DotNetName);
}
[Test]
public void IncompleteExtensionLambdaTest()
{
string program = @"using System;
using System.Collections.Generic;
static class TestClass {
static void Main(string[] args) {
args.Select(i => i
}
public static IEnumerable<R> Select<T, R>(this IEnumerable<T> input, Func<T, R> selector) { }
public delegate R Func<T, R>(T arg);
}";
var lrr = Resolve<LocalResolveResult>(program, "i", 5, 20, ExpressionContext.Default);
Assert.AreEqual("System.String", lrr.ResolvedType.DotNetName);
}
}
}

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

@ -104,5 +104,20 @@ namespace ICSharpCode.SharpDevelop.Tests @@ -104,5 +104,20 @@ namespace ICSharpCode.SharpDevelop.Tests
}
Assert.AreEqual(positions[num], mrr.ResolvedMember.Region.BeginLine, msg);
}
[Test]
public void MultipleOverloadsWithImplicitLambda()
{
string program = @"class MainClass {
void Main() {
M(x=>x.ToUpper());
}
delegate R Func<T, R>(T arg);
int M(Func<int, int> f){ /* whatever ... */ }
string M(Func<string, string> f){ /* whatever ... */ }
}";
var mrr = nrrt.Resolve<MemberResolveResult>(program, "M(x=>x.ToUpper())", 3, 3, ExpressionContext.Default);
Assert.AreEqual("System.String", mrr.ResolvedType.DotNetName);
}
}
}

5
src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj

@ -63,6 +63,8 @@ @@ -63,6 +63,8 @@
<Compile Include="..\..\GlobalAssemblyInfo.cs">
<Link>Configuration\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Src\CSharp\OverloadResolution.cs" />
<Compile Include="Src\CSharp\TypeInference.cs" />
<Compile Include="Src\ExtensionMethods.cs" />
<Compile Include="Src\Implementations\AbstractEntity.cs" />
<Compile Include="Src\Implementations\AbstractMember.cs" />
@ -89,6 +91,7 @@ @@ -89,6 +91,7 @@
<Compile Include="Src\Implementations\ElementReturnType.cs" />
<Compile Include="Src\Implementations\GenericReturnType.cs" />
<Compile Include="Src\Implementations\GetClassReturnType.cs" />
<Compile Include="Src\Implementations\MethodGroupReturnType.cs" />
<Compile Include="Src\Implementations\NullReturnType.cs" />
<Compile Include="Src\Implementations\ProxyReturnType.cs" />
<Compile Include="Src\Implementations\ReferenceReturnType.cs" />
@ -100,6 +103,8 @@ @@ -100,6 +103,8 @@
<Compile Include="Src\LazyList.cs" />
<Compile Include="Src\NRefactoryResolver\CSharpToVBNetConvertVisitor.cs" />
<Compile Include="Src\NRefactoryResolver\InferredReturnType.cs" />
<Compile Include="Src\NRefactoryResolver\LambdaParameterReturnType.cs" />
<Compile Include="Src\NRefactoryResolver\LambdaReturnType.cs" />
<Compile Include="Src\NRefactoryResolver\NRefactoryASTConvertVisitor.cs" />
<Compile Include="Src\NRefactoryResolver\NRefactoryInformationProvider.cs" />
<Compile Include="Src\NRefactoryResolver\NRefactoryResolver.cs" />

330
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/OverloadResolution.cs

@ -0,0 +1,330 @@ @@ -0,0 +1,330 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Linq;
using System.Collections.Generic;
namespace ICSharpCode.SharpDevelop.Dom.CSharp
{
/// <summary>
/// Implements C# 3.0 overload resolution.
/// </summary>
public sealed class OverloadResolution
{
private OverloadResolution() {}
public static IMethodOrProperty FindOverload(IList<IMethodOrProperty> list,
IReturnType[] arguments,
bool allowAdditionalArguments,
bool substituteInferredTypes,
out bool acceptableMatch)
{
OverloadResolution or = new OverloadResolution();
or.candidates = list.Select(m => new Candidate(m)).ToList();
or.arguments = arguments;
or.allowAdditionalArguments = allowAdditionalArguments;
if (or.candidates.Count == 0)
throw new ArgumentException("at least one candidate is required");
MemberLookupHelper.Log("OverloadResolution");
MemberLookupHelper.Log(" arguments = ", arguments);
or.ConstructExpandedForms();
or.InferTypeArguments();
or.CheckApplicability();
Candidate result = or.FindBestCandidate();
MemberLookupHelper.Log("Overload resolution finished. Winning candidate = " + result);
acceptableMatch = result.Status == CandidateStatus.Success;
if (substituteInferredTypes)
return result.Method;
else
return result.OriginalMethod;
}
enum CandidateStatus
{
Success,
WrongParameterCount,
TypeInferenceFailed,
NotApplicable
}
sealed class Candidate
{
public bool IsExpanded;
public IMethodOrProperty Method;
public IMethodOrProperty OriginalMethod;
public CandidateStatus Status = CandidateStatus.Success;
public int ApplicableArgumentCount;
public IList<IParameter> Parameters {
get { return Method.Parameters; }
}
public int TypeParameterCount {
get {
IMethod m = Method as IMethod;
if (m != null)
return m.TypeParameters.Count;
else
return 0;
}
}
public Candidate(IMethodOrProperty method)
{
this.Method = method;
this.OriginalMethod = method;
}
public override string ToString()
{
return "[Candidate: " + Method + ", Status=" + Status + "]";
}
}
List<Candidate> candidates;
IList<IReturnType> arguments;
bool allowAdditionalArguments;
/// <summary>
/// For methods having a params-array as last parameter, expand the params array to
/// n parameters of the element type and add those as new candidates.
/// Mark candidates with the wrong parameter count as Status.WrongParameterCount.
/// </summary>
void ConstructExpandedForms()
{
LogStep("Step 1 (Construct expanded forms)");
foreach (Candidate candidate in candidates.ToArray()) {
if (candidate.Status == CandidateStatus.Success) {
if (candidate.Parameters.Count > 0 && arguments.Count >= candidate.Parameters.Count - 1) {
IParameter lastParameter = candidate.Parameters[candidate.Parameters.Count - 1];
if (lastParameter.IsParams && lastParameter.ReturnType.IsArrayReturnType) {
// try to construct an expanded form with the correct parameter count
IReturnType elementType = lastParameter.ReturnType.CastToArrayReturnType().ArrayElementType;
IMethodOrProperty expanded = (IMethodOrProperty)candidate.Method.CreateSpecializedMember();
expanded.Parameters.RemoveAt(candidate.Parameters.Count - 1);
int index = 0;
while (expanded.Parameters.Count < arguments.Count) {
expanded.Parameters.Add(new DefaultParameter(lastParameter.Name + (index++), elementType, lastParameter.Region));
}
candidates.Add(new Candidate(expanded) {
IsExpanded = true,
OriginalMethod = candidate.Method
});
}
}
if (allowAdditionalArguments) {
if (candidate.Parameters.Count < arguments.Count) {
candidate.Status = CandidateStatus.WrongParameterCount;
}
} else {
if (candidate.Parameters.Count != arguments.Count) {
candidate.Status = CandidateStatus.WrongParameterCount;
}
}
}
}
}
/// <summary>
/// Infer the type arguments for generic methods.
/// </summary>
void InferTypeArguments()
{
LogStep("Step 2 (Infer type arguments)");
foreach (Candidate candidate in candidates) {
IMethod method = candidate.Method as IMethod;
if (method != null && method.TypeParameters.Count > 0
&& candidate.Status == CandidateStatus.Success)
{
bool success;
IReturnType[] typeArguments = TypeInference.InferTypeArguments(method, arguments, out success);
if (!success) {
candidate.Status = CandidateStatus.TypeInferenceFailed;
}
if (typeArguments != null) {
// apply inferred type arguments
method = (IMethod)method.CreateSpecializedMember();
method.ReturnType = ConstructedReturnType.TranslateType(method.ReturnType, typeArguments, true);
for (int i = 0; i < method.Parameters.Count; ++i) {
method.Parameters[i].ReturnType = ConstructedReturnType.TranslateType(method.Parameters[i].ReturnType, typeArguments, true);
}
candidate.Method = method;
}
}
}
}
void CheckApplicability()
{
LogStep("Step 3 (CheckApplicability)");
foreach (Candidate candidate in candidates) {
int c = Math.Min(arguments.Count, candidate.Parameters.Count);
for (int i = 0; i < c; i++) {
if (MemberLookupHelper.IsApplicable(arguments[i], candidate.Parameters[i], candidate.Method as IMethod))
candidate.ApplicableArgumentCount++;
}
if (candidate.Status == CandidateStatus.Success && candidate.ApplicableArgumentCount < arguments.Count) {
candidate.Status = CandidateStatus.NotApplicable;
}
}
}
Candidate FindBestCandidate()
{
LogStep("Step 4 (FindBestCandidate)");
// Find a candidate that is better than all other candidates
Candidate best = null;
foreach (Candidate candidate in candidates) {
if (candidate.Status == CandidateStatus.Success) {
if (best == null || GetBetterFunctionMember(best, candidate) == 2) {
best = candidate;
}
}
}
if (best != null)
return best;
// no successful candidate found:
// find the candidate that is nearest to being applicable
// first try only candidates with the correct parameter count
foreach (Candidate candidate in candidates) {
if (candidate.Status != CandidateStatus.WrongParameterCount) {
if (best == null || candidate.ApplicableArgumentCount > best.ApplicableArgumentCount)
best = candidate;
}
}
if (best != null)
return best;
// if all candidates have the wrong parameter count, return the candidate
// with the most applicable parameters.
best = candidates[0];
foreach (Candidate candidate in candidates) {
if (candidate.ApplicableArgumentCount > best.ApplicableArgumentCount)
best = candidate;
}
return best;
}
/// <summary>
/// Gets which function member is better. (§ 14.4.2.2)
/// </summary>
/// <returns>0 if neither method is better. 1 if c1 is better. 2 if c2 is better.</returns>
int GetBetterFunctionMember(Candidate c1, Candidate c2)
{
int length = Math.Min(Math.Min(c1.Parameters.Count, c2.Parameters.Count), arguments.Count);
bool foundBetterParamIn1 = false;
bool foundBetterParamIn2 = false;
for (int i = 0; i < length; i++) {
if (arguments[i] == null)
continue;
int res = MemberLookupHelper.GetBetterConversion(arguments[i], c1.Parameters[i].ReturnType, c2.Parameters[i].ReturnType);
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(c1.Parameters[i].ReturnType, c2.Parameters[i].ReturnType)) {
// 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 = c1.TypeParameterCount > 0;
bool m2IsGeneric = c2.TypeParameterCount > 0;
if (m1IsGeneric && !m2IsGeneric) return 2;
if (m2IsGeneric && !m1IsGeneric) return 1;
// for params parameters: non-expanded calls are better
if (c1.IsExpanded && !c2.IsExpanded) return 2;
if (c2.IsExpanded && !c1.IsExpanded) 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 (c1.OriginalMethod.Parameters.Count > c2.OriginalMethod.Parameters.Count) return 1;
if (c2.OriginalMethod.Parameters.Count > c1.OriginalMethod.Parameters.Count) return 2;
IReturnType[] m1ParamTypes = new IReturnType[c1.Parameters.Count];
IReturnType[] m2ParamTypes = new IReturnType[c2.Parameters.Count];
for (int i = 0; i < m1ParamTypes.Length; i++) {
m1ParamTypes[i] = c1.Parameters[i].ReturnType;
m2ParamTypes[i] = c2.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 == null && s == null) return 0;
if (r == null) return 2;
if (s == null) return 1;
if (r.IsGenericReturnType && !(s.IsGenericReturnType))
return 2;
if (s.IsGenericReturnType && !(r.IsGenericReturnType))
return 1;
if (r.IsArrayReturnType && s.IsArrayReturnType)
return GetMoreSpecific(r.CastToArrayReturnType().ArrayElementType, s.CastToArrayReturnType().ArrayElementType);
if (r.IsConstructedReturnType && s.IsConstructedReturnType)
return GetMoreSpecific(r.CastToConstructedReturnType().TypeArguments, s.CastToConstructedReturnType().TypeArguments);
return 0;
}
[System.Diagnostics.ConditionalAttribute("DEBUG")]
void LogStep(string title)
{
MemberLookupHelper.Log(" candidates = ", candidates);
MemberLookupHelper.Log("Overload resolution (" + title + ")");
}
[System.Diagnostics.ConditionalAttribute("DEBUG")]
void Log(string text)
{
MemberLookupHelper.Log(text);
}
}
}

480
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CSharp/TypeInference.cs

@ -0,0 +1,480 @@ @@ -0,0 +1,480 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
namespace ICSharpCode.SharpDevelop.Dom.CSharp
{
/// <summary>
/// Implements C# 3.0 type inference.
/// </summary>
sealed class TypeInference
{
private TypeInference() {}
public static IReturnType[] InferTypeArguments(IMethod method, IList<IReturnType> arguments, out bool success)
{
TypeInference ti = new TypeInference();
Log("Doing type inference for " + new CSharpAmbience().Convert(method));
Log(" with arguments = ", arguments);
ti.typeParameters = method.TypeParameters.Select(tp => new TP(tp)).ToList();
ti.parameterTypes = method.Parameters.Select(p => p.ReturnType).Take(arguments.Count).ToList();
ti.arguments = arguments.Take(ti.parameterTypes.Count).ToArray();
ti.PhaseOne();
if (ti.PhaseTwo()) {
IReturnType[] result = ti.typeParameters.Select(tp => tp.FixedTo).ToArray();
Log("Type inference for " + method.DotNetName + " succeeded: ", result);
success = true;
return result;
} else {
Log("Type inference for " + method.DotNetName + " failed.");
success = false;
return null;
}
}
List<TP> typeParameters;
List<IReturnType> parameterTypes;
IList<IReturnType> arguments;
sealed class TP {
public readonly ITypeParameter TypeParameter;
public IReturnType FixedTo;
public HashSet<IReturnType> Bounds = new HashSet<IReturnType>();
public bool Fixed {
get { return FixedTo != null; }
}
public TP(ITypeParameter typeParameter)
{
this.TypeParameter = typeParameter;
}
/// <summary>
/// Gets whether this type parameter occurs in the specified return type.
/// </summary>
public bool OccursIn(IReturnType rt)
{
ArrayReturnType art = rt.CastToArrayReturnType();
if (art != null) {
return OccursIn(art.ArrayElementType);
}
ConstructedReturnType crt = rt.CastToConstructedReturnType();
if (crt != null) {
return crt.TypeArguments.Any(ta => OccursIn(ta));
}
GenericReturnType grt = rt.CastToGenericReturnType();
if (grt != null) {
return this.TypeParameter.Equals(grt.TypeParameter);
}
return false;
}
public override string ToString()
{
return TypeParameter.Method.Name + "." + TypeParameter.Name;
}
}
void PhaseOne()
{
Log("Phase One");
for (int i = 0; i < arguments.Count; i++) {
IReturnType ei = arguments[i];
IReturnType Ti = parameterTypes[i];
if (ei is AnonymousMethodReturnType || ei is MethodGroupReturnType) {
Log("MakeExplicitParameterTypeInference for #" + i);
MakeExplicitParameterTypeInference(ei, Ti);
if (OutputTypeContainsUnfixed(ei, Ti) && !InputTypesContainsUnfixed(ei, Ti)) {
// an output type inference (§7.4.2.6) is made for ei with type Ti.
Log("MakeOutputTypeInference for #" + i);
MakeOutputTypeInference(ei, Ti);
}
} else {
Log("MakeOutputTypeInference for #" + i);
MakeOutputTypeInference(ei, Ti);
}
}
}
bool PhaseTwo()
{
Log("Phase Two");
// All unfixed type variables Xi which do not depend on any Xj are fixed.
List<TP> typeParametersToFix = new List<TP>();
foreach (TP Xi in typeParameters) {
if (Xi.Fixed == false) {
if (!typeParameters.Any((TP Xj) => DependsOn(Xi, Xj))) {
typeParametersToFix.Add(Xi);
}
}
}
// If no such type variables exist, all unfixed type variables Xi are fixed for which all of the following hold:
if (typeParametersToFix.Count == 0) {
foreach (TP Xi in typeParameters) {
// Xi has a non­empty set of bounds
if (Xi.Fixed == false && Xi.Bounds.Count > 0) {
// There is at least one type variable Xj that depends on Xi
if (typeParameters.Any((TP Xj) => DependsOn(Xj, Xi))) {
typeParametersToFix.Add(Xi);
}
}
}
}
// now fix 'em
foreach (TP tp in typeParametersToFix) {
if (!Fix(tp))
return false;
}
bool unfixedTypeVariablesExist = typeParameters.Any((TP X) => X.Fixed == false);
if (typeParametersToFix.Count == 0 && unfixedTypeVariablesExist) {
// If no such type variables exist and there are still unfixed type variables, type inference fails.
return false;
} else if (!unfixedTypeVariablesExist) {
// Otherwise, if no further unfixed type variables exist, type inference succeeds.
return true;
} else {
// Otherwise, for all arguments ei with corresponding parameter type Ti
for (int i = 0; i < arguments.Count; i++) {
IReturnType ei = arguments[i];
IReturnType Ti = parameterTypes[i];
// where the output types (§7.4.2.4) contain unfixed type variables Xj
// but the input types (§7.4.2.3) do not
if (OutputTypeContainsUnfixed(ei, Ti) && !InputTypesContainsUnfixed(ei, Ti)) {
// an output type inference (§7.4.2.6) is made for ei with type Ti.
Log("MakeOutputTypeInference for #" + i);
MakeOutputTypeInference(ei, Ti);
}
}
// Then the second phase is repeated.
return PhaseTwo();
}
}
bool OutputTypeContainsUnfixed(IReturnType argumentType, IReturnType parameterType)
{
return OutputTypes(argumentType, parameterType).Any(t => TypeContainsUnfixedParameter(t));
}
bool InputTypesContainsUnfixed(IReturnType argumentType, IReturnType parameterType)
{
return InputTypes(argumentType, parameterType).Any(t => TypeContainsUnfixedParameter(t));
}
bool TypeContainsUnfixedParameter(IReturnType type)
{
return typeParameters.Where(tp => !tp.Fixed).Any(tp => tp.OccursIn(type));
}
IEnumerable<IReturnType> OutputTypes(IReturnType e, IReturnType T)
{
AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType;
if (amrt != null || e is MethodGroupReturnType) {
IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt != null && amrt.CanBeConvertedToExpressionTree);
if (m != null) {
return new[] { m.ReturnType };
}
}
return EmptyList<IReturnType>.Instance;
}
IEnumerable<IReturnType> InputTypes(IReturnType e, IReturnType T)
{
AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType;
if (amrt != null && amrt.HasImplicitlyTypedParameters || e is MethodGroupReturnType) {
IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt != null && amrt.CanBeConvertedToExpressionTree);
if (m != null) {
return m.Parameters.Select(p => p.ReturnType);
}
}
return EmptyList<IReturnType>.Instance;
}
internal static IMethod GetDelegateOrExpressionTreeSignature(IReturnType rt, bool allowExpressionTree)
{
if (rt == null)
return null;
IClass c = rt.GetUnderlyingClass();
if (c != null && c.ClassType == ClassType.Delegate) {
return rt.GetMethods().FirstOrDefault((IMethod m) => m.Name == "Invoke");
}
// TODO: handle expression trees
return null;
}
bool DependsDirectlyOn(TP Xi, TP Xj)
{
if (Xj.Fixed)
return false;
for (int k = 0; k < arguments.Count; k++) {
if (InputTypes(arguments[k], parameterTypes[k]).Any(t => Xj.OccursIn(t))
&& OutputTypes(arguments[k], parameterTypes[k]).Any(t => Xi.OccursIn(t)))
{
return true;
}
}
return false;
}
void AddDependencies(HashSet<TP> hash, TP Xi)
{
foreach (TP Xj in typeParameters) {
if (DependsDirectlyOn(Xi, Xj)) {
if (hash.Add(Xj))
AddDependencies(hash, Xj);
}
}
}
HashSet<TP> GetDependencies(TP X)
{
HashSet<TP> hash = new HashSet<TP>();
AddDependencies(hash, X);
return hash;
}
bool DependsOn(TP Xi, TP Xj)
{
return GetDependencies(Xi).Contains(Xj);
}
void MakeOutputTypeInference(IReturnType e, IReturnType T)
{
//If e is an anonymous function with inferred return type  U (§7.4.2.11) and T is
// a delegate type or expression tree type with return type Tb, then a lower­bound
// inference (§7.4.2.9) is made from U for Tb.
AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType;
if (amrt != null) {
IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt.CanBeConvertedToExpressionTree);
if (m != null) {
IReturnType inferredReturnType;
if (amrt.HasParameterList && amrt.MethodParameters.Count == m.Parameters.Count) {
var inferredParameterTypes = m.Parameters.Select(p => SubstituteFixedTypes(p.ReturnType)).ToArray();
Log(" infer rt from lambda with parameters ", inferredParameterTypes);
inferredReturnType = amrt.ResolveReturnType(inferredParameterTypes);
} else {
Log(" infer rt from anonymous method");
inferredReturnType = amrt.ResolveReturnType();
}
Log(" inferred " + inferredReturnType);
MakeLowerBoundInference(inferredReturnType, m.ReturnType);
return;
}
}
// Otherwise, if e is a method group and T is a delegate type or expression tree type
// return type Tb with parameter types T1…Tk and return type Tb, and overload resolution
// of e with the types T1…Tk yields a single method with return type U, then a lower­bound
// inference is made from U for Tb.
if (e is MethodGroupReturnType) {
// the MS C# doesn't seem to implement this rule, so we can safely skip this
return;
}
// Otherwise, if e is an expression with type U, then a lower­bound inference is made from
// U for T.
MakeLowerBoundInference(e, T);
}
IReturnType SubstituteFixedTypes(IReturnType rt)
{
return ConstructedReturnType.TranslateType(
rt, typeParameters.Select(tp => tp.FixedTo).ToList(), true);
}
void MakeExplicitParameterTypeInference(IReturnType e, IReturnType T)
{
// If e is an explicitly typed anonymous function with parameter types U1…Uk and T is a
// delegate type with parameter types V1…Vk then for each Ui an exact inference (§7.4.2.8)
// is made from Ui for the corresponding Vi.
AnonymousMethodReturnType amrt = e as AnonymousMethodReturnType;
if (amrt != null && amrt.HasParameterList) {
IMethod m = GetDelegateOrExpressionTreeSignature(T, amrt.CanBeConvertedToExpressionTree);
if (m != null && amrt.MethodParameters.Count == m.Parameters.Count) {
for (int i = 0; i < amrt.MethodParameters.Count; i++) {
MakeExactInference(amrt.MethodParameters[i].ReturnType, m.Parameters[i].ReturnType);
}
}
}
}
/// <summary>
/// Make exact inference from U for V.
/// </summary>
void MakeExactInference(IReturnType U, IReturnType V)
{
Log(" MakeExactInference from " + U + " for " + V);
if (U == null || V == null)
return;
// If V is one of the unfixed Xi then U is added to the set of bounds for Xi.
TP tp = GetTPForType(V);
if (tp != null && tp.Fixed == false) {
Log(" Add bound '" + U.DotNetName + "' to " + tp);
tp.Bounds.Add(U);
return;
}
// Otherwise if U is an array type Ue[…] and V is an array type Ve[…] of the same rank
// then an exact inference from Ue to Ve is made
ArrayReturnType arrU = U.CastToArrayReturnType();
ArrayReturnType arrV = V.CastToArrayReturnType();
if (arrU != null && arrV != null && arrU.ArrayDimensions == arrV.ArrayDimensions) {
MakeExactInference(arrU.ArrayElementType, arrV.ArrayElementType);
return;
}
// Otherwise if V is a constructed type C<V1…Vk> and U is a constructed
// type C<U1…Uk> then an exact inference is made from each Ui to the corresponding Vi.
ConstructedReturnType CU = U.CastToConstructedReturnType();
ConstructedReturnType CV = V.CastToConstructedReturnType();
if (CU != null && CV != null
&& object.Equals(CU.UnboundType, CV.UnboundType)
&& CU.TypeArgumentCount == CV.TypeArgumentCount)
{
for (int i = 0; i < CU.TypeArgumentCount; i++) {
MakeExactInference(CU.TypeArguments[i], CV.TypeArguments[i]);
}
return;
}
}
TP GetTPForType(IReturnType t)
{
if (t == null)
return null;
GenericReturnType grt = t.CastToGenericReturnType();
if (grt != null) {
return typeParameters.FirstOrDefault(tp => tp.TypeParameter.Equals(grt.TypeParameter));
}
return null;
}
/// <summary>
/// Make exact inference from U for V.
/// </summary>
void MakeLowerBoundInference(IReturnType U, IReturnType V)
{
Log(" MakeLowerBoundInference from " + U + " for " + V);
if (U == null || V == null)
return;
// If V is one of the unfixed Xi then U is added to the set of bounds for Xi.
TP tp = GetTPForType(V);
if (tp != null && tp.Fixed == false) {
Log(" Add bound '" + U.DotNetName + "' to " + tp);
tp.Bounds.Add(U);
return;
}
// Otherwise if U is an array type Ue[…] and V is either an array type Ve[…]of the
// same rank, or if U is a one­dimensional array type Ue[]and V is one of
// IEnumerable<Ve>, ICollection<Ve> or IList<Ve> then
ArrayReturnType arrU = U.CastToArrayReturnType();
ArrayReturnType arrV = V.CastToArrayReturnType();
ConstructedReturnType CV = V.CastToConstructedReturnType();
if (arrU != null &&
(arrV != null && arrU.ArrayDimensions == arrV.ArrayDimensions
|| (arrU.ArrayDimensions == 1 && IsIEnumerableCollectionOrList(CV))))
{
IReturnType Ue = arrU.ArrayElementType;
IReturnType Ve = arrV != null ? arrV.ArrayElementType : CV.TypeArguments[0];
// If Ue is known to be a reference type then a lower­bound inference from Ue to Ve is made
if (IsReferenceType(Ue) ?? false) {
MakeLowerBoundInference(Ue, Ve);
} else {
// Otherwise an exact inference from Ue to Ve is made
MakeExactInference(Ue, Ve);
}
return;
}
// Otherwise if V is a constructed type C<V1…Vk> and there is a unique set of
// types U1…Uk such that a standard implicit conversion exists from U to C<U1…Uk>
// then an exact inference is made from each Ui for the corresponding Vi.
if (CV != null) {
// TODO: implement this rule
// For now, I'm just copying the exact inference code here
ConstructedReturnType CU = U.CastToConstructedReturnType();
if (CU != null && CV != null
&& object.Equals(CU.UnboundType, CV.UnboundType)
&& CU.TypeArgumentCount == CV.TypeArgumentCount)
{
for (int i = 0; i < CU.TypeArgumentCount; i++) {
MakeExactInference(CU.TypeArguments[i], CV.TypeArguments[i]);
}
return;
}
}
}
bool IsIEnumerableCollectionOrList(ConstructedReturnType rt)
{
if (rt == null || rt.TypeArgumentCount != 1)
return false;
switch (rt.UnboundType.FullyQualifiedName) {
case "System.Collections.Generic.IList":
case "System.Collections.Generic.ICollection":
case "System.Collections.Generic.IEnumerable":
return true;
default:
return false;
}
}
bool? IsReferenceType(IReturnType rt)
{
if (rt == null)
return null;
IClass c = rt.GetUnderlyingClass();
if (c == null)
return null;
switch (c.ClassType) {
case ClassType.Enum:
case ClassType.Struct:
return false;
default:
return true;
}
}
bool Fix(TP X)
{
Log("Trying to fix " + X);
Log(" bounds = ", X.Bounds);
List<IReturnType> candidates = new List<IReturnType>(X.Bounds);
foreach (IReturnType U in X.Bounds) {
candidates.RemoveAll((IReturnType candidate) => !MemberLookupHelper.ConversionExists(U, candidate));
}
Log(" candidates after removal round = ", candidates);
if (candidates.Count == 0)
return false;
var results = candidates.Where(
c1 => candidates.All(c2 => MemberLookupHelper.ConversionExists(c1, c2))
).ToList();
Log(" possible solutions (should be exactly one) = ", candidates);
if (results.Count == 1) {
X.FixedTo = results[0];
return true;
} else {
return false;
}
}
[System.Diagnostics.ConditionalAttribute("DEBUG")]
static void Log(string text)
{
MemberLookupHelper.Log(text);
}
[System.Diagnostics.ConditionalAttribute("DEBUG")]
static void Log(string text, IEnumerable<IReturnType> types)
{
MemberLookupHelper.Log(text, types);
}
}
}

5
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CtrlSpaceResolveHelper.cs

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace ICSharpCode.SharpDevelop.Dom
{
@ -184,7 +185,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -184,7 +185,7 @@ namespace ICSharpCode.SharpDevelop.Dom
if (callingClass == null)
throw new ArgumentNullException("callingClass");
List<IMethodOrProperty> res = new List<IMethodOrProperty>();
HashSet<IMethodOrProperty> res = new HashSet<IMethodOrProperty>();
bool supportsExtensionMethods = language.SupportsExtensionMethods;
bool supportsExtensionProperties = language.SupportsExtensionProperties;
@ -220,7 +221,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -220,7 +221,7 @@ namespace ICSharpCode.SharpDevelop.Dom
}
}
}
return res;
return res.ToList();
}
}
}

34
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/AnonymousMethodReturnType.cs

@ -8,13 +8,14 @@ @@ -8,13 +8,14 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// The return type of anonymous method expressions or lambda expressions.
/// </summary>
public sealed class AnonymousMethodReturnType : DecoratingReturnType
public class AnonymousMethodReturnType : DecoratingReturnType
{
IReturnType returnType;
IList<IParameter> parameters;
@ -51,12 +52,18 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -51,12 +52,18 @@ namespace ICSharpCode.SharpDevelop.Dom
/// Return type of the anonymous method. Can be null if inferred from context.
/// </summary>
public IReturnType MethodReturnType {
get {
return returnType;
}
set {
returnType = value;
}
get { return returnType; }
set { returnType = value; }
}
public virtual IReturnType ResolveReturnType()
{
return returnType;
}
public virtual IReturnType ResolveReturnType(IReturnType[] parameterTypes)
{
return returnType;
}
/// <summary>
@ -67,10 +74,23 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -67,10 +74,23 @@ namespace ICSharpCode.SharpDevelop.Dom
set { parameters = value; }
}
public virtual bool CanBeConvertedToExpressionTree {
get { return false; }
}
public bool HasParameterList {
get { return parameters != null; }
}
public bool HasImplicitlyTypedParameters {
get {
if (parameters == null)
return false;
else
return parameters.Any(p => p.ReturnType == null);
}
}
DefaultClass cachedClass;
public override IClass GetUnderlyingClass()

2
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ArrayReturnType.cs

@ -143,7 +143,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -143,7 +143,7 @@ namespace ICSharpCode.SharpDevelop.Dom
public override string ToString()
{
return String.Format("[ArrayReturnType: {0}, dimensions={1}]", elementType, AppendArrayString(""));
return String.Format("[ArrayReturnType: {0}{1}]", elementType, AppendArrayString(""));
}
}
}

10
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultMethod.cs

@ -184,11 +184,11 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -184,11 +184,11 @@ namespace ICSharpCode.SharpDevelop.Dom
public override string ToString()
{
return String.Format("[AbstractMethod: FullyQualifiedName={0}, ReturnType = {1}, IsConstructor={2}, Modifier={3}]",
FullyQualifiedName,
ReturnType,
IsConstructor,
base.Modifiers);
return String.Format("[DefaultMethod: {0}]",
(new Dom.CSharp.CSharpAmbience {
ConversionFlags = ConversionFlags.StandardConversionFlags
| ConversionFlags.UseFullyQualifiedMemberNames
}).Convert(this));
}
public virtual int CompareTo(IMethod value)

4
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultTypeParameter.cs

@ -143,9 +143,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -143,9 +143,9 @@ namespace ICSharpCode.SharpDevelop.Dom
if (tp.hasValueTypeConstraint != hasValueTypeConstraint) return false;
if (tp.method != method) {
if (tp.method == null || method == null) return false;
if (tp.method.FullyQualifiedName == method.FullyQualifiedName) return false;
if (tp.method.FullyQualifiedName != method.FullyQualifiedName) return false;
} else {
if (tp.targetClass.FullyQualifiedName == targetClass.FullyQualifiedName) return false;
if (tp.targetClass.FullyQualifiedName != targetClass.FullyQualifiedName) return false;
}
return true;
}

49
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/MethodGroupReturnType.cs

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
/*
* Created by SharpDevelop.
* User: me
* Date: 3/29/2008
* Time: 7:12 PM
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Collections.Generic;
namespace ICSharpCode.SharpDevelop.Dom
{
/// <summary>
/// Return type used for MethodGroupResolveResult.
/// </summary>
public class MethodGroupReturnType : AbstractReturnType
{
public MethodGroupReturnType()
{
}
public override IClass GetUnderlyingClass()
{
return null;
}
public override List<IMethod> GetMethods()
{
return new List<IMethod>();
}
public override List<IProperty> GetProperties()
{
return new List<IProperty>();
}
public override List<IField> GetFields()
{
return new List<IField>();
}
public override List<IEvent> GetEvents()
{
return new List<IEvent>();
}
}
}

35
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/ProxyReturnType.cs

@ -34,7 +34,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -34,7 +34,7 @@ namespace ICSharpCode.SharpDevelop.Dom
IReturnType baseType = BaseType;
bool tmp = (baseType != null && TryEnter()) ? baseType.Equals(other) : false;
busy = false;
Leave();
return tmp;
}
@ -42,7 +42,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -42,7 +42,7 @@ namespace ICSharpCode.SharpDevelop.Dom
{
IReturnType baseType = BaseType;
int tmp = (baseType != null && TryEnter()) ? baseType.GetHashCode() : 0;
busy = false;
Leave();
return tmp;
}
@ -52,7 +52,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -52,7 +52,7 @@ namespace ICSharpCode.SharpDevelop.Dom
}
// Required to prevent stack overflow on inferrence cycles
bool busy = false;
bool busy;
// keep this method as small as possible, it should be inlined!
bool TryEnter()
@ -66,6 +66,11 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -66,6 +66,11 @@ namespace ICSharpCode.SharpDevelop.Dom
}
}
void Leave()
{
busy = false;
}
void PrintTryEnterWarning()
{
LoggingService.Info("TryEnter failed on " + ToString());
@ -75,7 +80,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -75,7 +80,7 @@ namespace ICSharpCode.SharpDevelop.Dom
get {
IReturnType baseType = BaseType;
string tmp = (baseType != null && TryEnter()) ? baseType.FullyQualifiedName : "?";
busy = false;
Leave();
return tmp;
}
}
@ -84,7 +89,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -84,7 +89,7 @@ namespace ICSharpCode.SharpDevelop.Dom
get {
IReturnType baseType = BaseType;
string tmp = (baseType != null && TryEnter()) ? baseType.Name : "?";
busy = false;
Leave();
return tmp;
}
}
@ -93,7 +98,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -93,7 +98,7 @@ namespace ICSharpCode.SharpDevelop.Dom
get {
IReturnType baseType = BaseType;
string tmp = (baseType != null && TryEnter()) ? baseType.Namespace : "?";
busy = false;
Leave();
return tmp;
}
}
@ -102,7 +107,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -102,7 +107,7 @@ namespace ICSharpCode.SharpDevelop.Dom
get {
IReturnType baseType = BaseType;
string tmp = (baseType != null && TryEnter()) ? baseType.DotNetName : "?";
busy = false;
Leave();
return tmp;
}
}
@ -111,7 +116,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -111,7 +116,7 @@ namespace ICSharpCode.SharpDevelop.Dom
get {
IReturnType baseType = BaseType;
int tmp = (baseType != null && TryEnter()) ? baseType.TypeArgumentCount : 0;
busy = false;
Leave();
return tmp;
}
}
@ -120,7 +125,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -120,7 +125,7 @@ namespace ICSharpCode.SharpDevelop.Dom
{
IReturnType baseType = BaseType;
IClass tmp = (baseType != null && TryEnter()) ? baseType.GetUnderlyingClass() : null;
busy = false;
Leave();
return tmp;
}
@ -128,7 +133,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -128,7 +133,7 @@ namespace ICSharpCode.SharpDevelop.Dom
{
IReturnType baseType = BaseType;
List<IMethod> tmp = (baseType != null && TryEnter()) ? baseType.GetMethods() : new List<IMethod>();
busy = false;
Leave();
return tmp;
}
@ -136,7 +141,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -136,7 +141,7 @@ namespace ICSharpCode.SharpDevelop.Dom
{
IReturnType baseType = BaseType;
List<IProperty> tmp = (baseType != null && TryEnter()) ? baseType.GetProperties() : new List<IProperty>();
busy = false;
Leave();
return tmp;
}
@ -144,7 +149,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -144,7 +149,7 @@ namespace ICSharpCode.SharpDevelop.Dom
{
IReturnType baseType = BaseType;
List<IField> tmp = (baseType != null && TryEnter()) ? baseType.GetFields() : new List<IField>();
busy = false;
Leave();
return tmp;
}
@ -152,7 +157,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -152,7 +157,7 @@ namespace ICSharpCode.SharpDevelop.Dom
{
IReturnType baseType = BaseType;
List<IEvent> tmp = (baseType != null && TryEnter()) ? baseType.GetEvents() : new List<IEvent>();
busy = false;
Leave();
return tmp;
}
@ -160,7 +165,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -160,7 +165,7 @@ namespace ICSharpCode.SharpDevelop.Dom
get {
IReturnType baseType = BaseType;
bool tmp = (baseType != null && TryEnter()) ? baseType.IsDefaultReturnType : false;
busy = false;
Leave();
return tmp;
}
}
@ -178,7 +183,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -178,7 +183,7 @@ namespace ICSharpCode.SharpDevelop.Dom
temp = baseType.CastToDecoratingReturnType<T>();
else
temp = null;
busy = false;
Leave();
return temp;
}

474
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs

@ -20,6 +20,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -20,6 +20,7 @@ namespace ICSharpCode.SharpDevelop.Dom
/// </remarks>
public static class MemberLookupHelper
{
#region LookupMember / GetAccessibleMembers
static List<IMember> GetAllMembers(IReturnType rt)
{
List<IMember> members = new List<IMember>();
@ -137,8 +138,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -137,8 +138,9 @@ namespace ICSharpCode.SharpDevelop.Dom
List<IList<IMember>> allResults = new List<IList<IMember>>();
List<IMember> results = new List<IMember>();
foreach (var group in accessibleMembers
.GroupBy((IMember m) => m.DeclaringType.GetCompoundClass())
.GroupBy(m => m.DeclaringType.GetCompoundClass())
.OrderByDescending(g => g.Key, InheritanceLevelComparer.Instance))
{
//Console.WriteLine("Member group " + group.Key);
@ -205,6 +207,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -205,6 +207,7 @@ namespace ICSharpCode.SharpDevelop.Dom
}
return result;
}
#endregion
#region FindOverload
/// <summary>
@ -223,356 +226,36 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -223,356 +226,36 @@ namespace ICSharpCode.SharpDevelop.Dom
resultIsAcceptable = false;
if (methods.Count == 0)
return null;
// RankOverloads may change methods (substitution of generic types), but those changes should not
// be visible to the FindOverload caller, so we clone the method array.
methods = methods.ToArray();
int[] ranking = RankOverloads(methods, arguments, false, out resultIsAcceptable);
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];
return (IMethod)CSharp.OverloadResolution.FindOverload(
methods.Cast<IMethodOrProperty>().ToList(),
arguments,
false,
true,
out resultIsAcceptable);
}
public static IProperty FindOverload(IList<IProperty> properties, IReturnType[] arguments)
{
if (properties.Count == 0)
return null;
bool tmp1; IReturnType[][] tmp2;
List<IMethodOrProperty> newList = new List<IMethodOrProperty>(properties.Count);
foreach (IProperty p in properties) newList.Add(p);
int[] ranking = RankOverloads(newList, arguments, false, out tmp1, out tmp2);
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">List 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="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[] arguments,
bool allowAdditionalArguments,
out bool acceptableMatch)
{
if (list == null)
throw new ArgumentNullException("list");
if (arguments == null)
throw new ArgumentNullException("arguments");
acceptableMatch = false;
if (list.Count == 0) return new int[] {};
IReturnType[][] inferredTypeParameters;
// See ECMA-334, § 14.3
// We longer pass the explicit type arguments to RankOverloads, this is now done when
// the method group is constructed.
// 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.
List<IMethodOrProperty> l2 = new List<IMethodOrProperty>();
foreach (IMethod m in list) l2.Add(m);
int[] ranking = RankOverloads(l2, arguments, allowAdditionalArguments, out acceptableMatch, out inferredTypeParameters);
ApplyInferredTypeParameters(list, inferredTypeParameters);
return ranking;
}
static void ApplyInferredTypeParameters(IList<IMethod> list, IReturnType[][] inferredTypeParameters)
{
if (inferredTypeParameters == null)
return;
for (int i = 0; i < list.Count; i++) {
IReturnType[] inferred = inferredTypeParameters[i];
if (inferred != null && inferred.Length > 0) {
IMethod m = (IMethod)list[i].CreateSpecializedMember();
m.ReturnType = ConstructedReturnType.TranslateType(m.ReturnType, inferred, true);
for (int j = 0; j < m.Parameters.Count; ++j) {
m.Parameters[j].ReturnType = ConstructedReturnType.TranslateType(m.Parameters[j].ReturnType, inferred, true);
}
list[i] = m;
}
}
}
#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,
out IReturnType[][] inferredTypeParameters)
{
// § 14.4.2 Overload resolution
acceptableMatch = false;
inferredTypeParameters = null;
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;
int score;
bool expanded;
for (int i = 0; i < list.Count; i++) {
if (IsApplicable(list[i], 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
IReturnType[][] expandedParameters = ExpandParametersAndSubstitute(list, arguments, maxScore, ranking, needToExpand, out inferredTypeParameters);
// 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, we would need to compare the member with other members
// but this works as we're interested in the best overload only
break;
}
}
}
}
return ranking;
}
static IReturnType[][] ExpandParametersAndSubstitute(IList<IMethodOrProperty> list,
IReturnType[] arguments,
int maxScore, int[] ranking, bool[] needToExpand,
out IReturnType[][] inferredTypeParameters)
{
IReturnType[][] expandedParameters = new IReturnType[list.Count][];
inferredTypeParameters = new IReturnType[list.Count][];
for (int i = 0; i < ranking.Length; i++) {
if (ranking[i] == maxScore) {
IList<IParameter> parameters = list[i].Parameters;
IReturnType[] typeParameters = (list[i] is IMethod) ? InferTypeArguments((IMethod)list[i], arguments) : null;
inferredTypeParameters[i] = typeParameters;
IReturnType paramsType = null;
expandedParameters[i] = new IReturnType[arguments.Length];
for (int j = 0; j < arguments.Length; j++) {
if (j < parameters.Count) {
IParameter parameter = parameters[j];
if (parameter.IsParams && needToExpand[i]) {
if (parameter.ReturnType.IsArrayReturnType) {
paramsType = parameter.ReturnType.CastToArrayReturnType().ArrayElementType;
paramsType = ConstructedReturnType.TranslateType(paramsType, typeParameters, true);
}
expandedParameters[i][j] = paramsType;
} else {
expandedParameters[i][j] = ConstructedReturnType.TranslateType(parameter.ReturnType, typeParameters, true);
}
} else {
expandedParameters[i][j] = paramsType;
}
}
}
}
return expandedParameters;
}
/// <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)
{
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 == null && s == null) return 0;
if (r == null) return 2;
if (s == null) return 1;
if (r.IsGenericReturnType && !(s.IsGenericReturnType))
return 2;
if (s.IsGenericReturnType && !(r.IsGenericReturnType))
return 1;
if (r.IsArrayReturnType && s.IsArrayReturnType)
return GetMoreSpecific(r.CastToArrayReturnType().ArrayElementType, s.CastToArrayReturnType().ArrayElementType);
if (r.IsConstructedReturnType && s.IsConstructedReturnType)
return GetMoreSpecific(r.CastToConstructedReturnType().TypeArguments, s.CastToConstructedReturnType().TypeArguments);
return 0;
bool acceptableMatch;
return (IProperty)CSharp.OverloadResolution.FindOverload(
properties.Cast<IMethodOrProperty>().ToList(),
arguments,
false,
false,
out acceptableMatch);
}
#endregion
#region Type Argument Inference
static IReturnType[] InferTypeArguments(IMethod method, IReturnType[] arguments)
{
// §25.6.4 Inference of type arguments
int count = method.TypeParameters.Count;
if (count == 0) return null;
IReturnType[] result = new IReturnType[count];
IList<IParameter> parameters = method.Parameters;
for (int i = 0; i < arguments.Length; i++) {
if (i >= parameters.Count)
break;
if (!InferTypeArgument(parameters[i].ReturnType, arguments[i], result)) {
// inferring failed: maybe this is a params parameter that must be expanded?
if (parameters[i].IsParams && parameters[i].ReturnType.IsArrayReturnType) {
ArrayReturnType art = parameters[i].ReturnType.CastToArrayReturnType();
if (art.ArrayDimensions == 1) {
InferTypeArgument(art.ArrayElementType, arguments[i], result);
}
}
}
}
// only return the result array when there something was inferred
for (int i = 0; i < result.Length; i++) {
if (result[i] != null) {
return result;
}
}
return null;
}
/// <summary>
/// Infers type arguments specified by passing expectedArgument as parameter where passedArgument
/// was expected. The resulting type arguments are written to outputArray.
/// Returns false when expectedArgument and passedArgument are incompatible, otherwise true
/// is returned (true is used both for successful inferring and other kind of errors).
///
/// Warning: This method for single-argument type inference doesn't support lambdas!
/// </summary>
/// <remarks>
/// The C# spec (§ 25.6.4) has a bug: it says that type inference works if the passedArgument is IEnumerable{T}
@ -627,66 +310,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -627,66 +310,7 @@ namespace ICSharpCode.SharpDevelop.Dom
#endregion
#region IsApplicable
static bool IsApplicable(IMethodOrProperty targetMethodOrProperty,
IReturnType[] arguments,
bool allowAdditionalArguments,
out int score,
out bool expanded)
{
// see ECMA-334, § 14.4.2.1
IList<IParameter> parameters = targetMethodOrProperty.Parameters;
IMethod targetMethod = targetMethodOrProperty as IMethod;
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], targetMethod)) {
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], targetMethod)) {
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
IReturnType rt = parameters[lastParameter].ReturnType;
if (rt == null || !rt.IsArrayReturnType) {
return false;
}
for (int i = lastParameter; i < arguments.Length; i++) {
if (IsApplicable(arguments[i], rt.CastToArrayReturnType().ArrayElementType, targetMethod)) {
score++;
} else {
ok = false;
}
}
return ok;
}
static bool IsApplicable(IReturnType argument, IParameter expected, IMethod targetMethod)
internal static bool IsApplicable(IReturnType argument, IParameter expected, IMethod targetMethod)
{
bool parameterIsRefOrOut = expected.IsRef || expected.IsOut;
bool argumentIsRefOrOut = argument != null && argument.IsDecoratingReturnType<ReferenceReturnType>();
@ -722,7 +346,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -722,7 +346,7 @@ namespace ICSharpCode.SharpDevelop.Dom
/// <summary>
/// Tests if an implicit conversion exists from "from" to "to".
/// Conversions from concrete types to generic types are only allowed when the generic type belongs to the
/// Conversions from concrete types to generic types are only allowed when the generic type belongs to the
/// method "allowGenericTargetsOnThisMethod".
/// </summary>
static bool ConversionExistsInternal(IReturnType from, IReturnType to, IMethod allowGenericTargetsOnThisMethod)
@ -801,19 +425,27 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -801,19 +425,27 @@ namespace ICSharpCode.SharpDevelop.Dom
}
if (from.IsDecoratingReturnType<AnonymousMethodReturnType>() && (toIsDefault || to.IsConstructedReturnType)) {
IList<IParameter> methodParameters = from.CastToDecoratingReturnType<AnonymousMethodReturnType>().MethodParameters;
IClass toClass = to.GetUnderlyingClass();
if (toClass != null && toClass.ClassType == ClassType.Delegate) {
if (methodParameters == null) {
return true;
} else {
foreach (IMethod m in toClass.Methods) {
if (m.Name == "Invoke") {
return m.Parameters.Count == methodParameters.Count;
AnonymousMethodReturnType amrt = from.CastToDecoratingReturnType<AnonymousMethodReturnType>();
IMethod method = CSharp.TypeInference.GetDelegateOrExpressionTreeSignature(to, amrt.CanBeConvertedToExpressionTree);
if (method != null) {
if (amrt.HasParameterList) {
if (amrt.MethodParameters.Count != method.Parameters.Count)
return false;
for (int i = 0; i < amrt.MethodParameters.Count; i++) {
if (amrt.MethodParameters[i].ReturnType != null) {
if (!object.Equals(amrt.MethodParameters[i].ReturnType,
method.Parameters[i].ReturnType))
{
return false;
}
}
}
return true;
}
IReturnType rt = amrt.ResolveReturnType(method.Parameters.Select(p => p.ReturnType).ToArray());
if (rt == null)
return false;
return true;
}
}
@ -1187,5 +819,33 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -1187,5 +819,33 @@ namespace ICSharpCode.SharpDevelop.Dom
return null;
}
#endregion
[System.Diagnostics.ConditionalAttribute("DEBUG")]
internal static void Log(string text)
{
Console.WriteLine(text);
}
[System.Diagnostics.ConditionalAttribute("DEBUG")]
internal static void Log(string text, IEnumerable<IReturnType> types)
{
Log(text, types.Select(t => t != null ? t.DotNetName : "<null>"));
}
[System.Diagnostics.ConditionalAttribute("DEBUG")]
internal static void Log<T>(string text, IEnumerable<T> lines)
{
#if DEBUG
T[] arr = lines.ToArray();
if (arr.Length == 0) {
Log(text + "<empty collection>");
} else {
Log(text + arr[0]);
for (int i = 1; i < arr.Length; i++) {
Log(new string(' ', text.Length) + arr[i]);
}
}
#endif
}
}
}

69
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaParameterReturnType.cs

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.NRefactory.Ast;
namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
{
/// <summary>
/// Description of LambdaParameterReturnType.
/// </summary>
public class LambdaParameterReturnType : ProxyReturnType
{
LambdaExpression lambda;
int parameterIndex;
string parameterName;
NRefactoryResolver resolver;
public LambdaParameterReturnType(LambdaExpression lambda, string name, NRefactoryResolver resolver)
{
if (lambda == null)
throw new ArgumentNullException("lambda");
if (name == null)
throw new ArgumentNullException("name");
if (resolver == null)
throw new ArgumentNullException("resolver");
this.lambda = lambda;
this.parameterName = name;
this.parameterIndex = lambda.Parameters.FindIndex(p => p.ParameterName == name);
this.resolver = resolver;
if (parameterIndex < 0)
throw new ArgumentException("there is no lambda parameter with that name");
}
IReturnType cachedType;
public override IReturnType BaseType {
get {
NRefactoryResolver resolver = this.resolver;
LambdaExpression lambda = this.lambda;
if (resolver == null || lambda == null)
return cachedType;
this.resolver = null;
this.lambda = null;
MemberLookupHelper.Log("Resolving " + this);
IReturnType rt = resolver.GetExpectedTypeFromContext(lambda);
MemberLookupHelper.Log("Resolving " + this + ", got delegate type " + rt);
IMethod sig = CSharp.TypeInference.GetDelegateOrExpressionTreeSignature(rt, true);
if (sig != null && parameterIndex < sig.Parameters.Count) {
MemberLookupHelper.Log("Resolving " + this + ", got type " + rt);
return cachedType = sig.Parameters[parameterIndex].ReturnType;
}
return null;
}
}
public override string ToString()
{
return "[LambdaParameterReturnType: " + parameterName +
(resolver != null ? " (not yet resolved)" : " (" + cachedType + ")")
+ "]";
}
}
}

100
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/LambdaReturnType.cs

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.NRefactory.Ast;
namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
{
public class LambdaReturnType : AnonymousMethodReturnType
{
NRefactoryResolver resolver;
LambdaExpression lambdaExpression;
List<Expression> returnExpressions = new List<Expression>();
internal LambdaReturnType(LambdaExpression expression, NRefactoryResolver resolver)
: base(resolver.CompilationUnit)
{
this.resolver = resolver;
this.lambdaExpression = expression;
base.MethodParameters = new List<IParameter>();
foreach (ParameterDeclarationExpression param in expression.Parameters) {
base.MethodParameters.Add(NRefactoryASTConvertVisitor.CreateParameter(param, resolver.CallingMember as IMethod, resolver.CallingClass, resolver.CompilationUnit));
}
if (expression.ExpressionBody.IsNull)
expression.StatementBody.AcceptVisitor(new ReturnStatementFinder(returnExpressions), null);
else
returnExpressions.Add(expression.ExpressionBody);
}
internal LambdaReturnType(AnonymousMethodExpression expression, NRefactoryResolver resolver)
: base(resolver.CompilationUnit)
{
this.resolver = resolver;
if (expression.HasParameterList) {
base.MethodParameters = new List<IParameter>();
foreach (ParameterDeclarationExpression param in expression.Parameters) {
base.MethodParameters.Add(NRefactoryASTConvertVisitor.CreateParameter(param, resolver.CallingMember as IMethod, resolver.CallingClass, resolver.CompilationUnit));
}
}
expression.Body.AcceptVisitor(new ReturnStatementFinder(returnExpressions), null);
}
sealed class ReturnStatementFinder : AbstractAstVisitor
{
List<Expression> returnExpressions;
public ReturnStatementFinder(List<Expression> returnExpressions)
{
this.returnExpressions = returnExpressions;
}
public override object VisitReturnStatement(ReturnStatement returnStatement, object data)
{
returnExpressions.Add(returnStatement.Expression);
return base.VisitReturnStatement(returnStatement, data);
}
public override object VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data)
{
return null;
}
public override object VisitLambdaExpression(LambdaExpression lambdaExpression, object data)
{
return null;
}
}
public override IReturnType ResolveReturnType(IReturnType[] parameterTypes)
{
if (lambdaExpression == null)
return ResolveReturnType();
try {
resolver.SetImplicitLambdaParameterTypes(lambdaExpression, parameterTypes);
return ResolveReturnType();
} finally {
resolver.UnsetImplicitLambdaParameterTypes(lambdaExpression);
}
}
public override IReturnType ResolveReturnType()
{
if (returnExpressions.Count == 0)
return resolver.ProjectContent.SystemTypes.Void;
return returnExpressions.Select(rt => resolver.ResolveInternal(rt, ExpressionContext.Default))
.Select(rr => rr != null ? rr.ResolvedType : null)
.Aggregate((rt1, rt2) => MemberLookupHelper.GetCommonType(resolver.ProjectContent, rt1, rt2));
}
}
}

91
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs

@ -574,7 +574,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -574,7 +574,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
do {
ResolveResult rr = ResolveMember(tmp.DefaultReturnType, identifier,
identifierExpression.TypeArguments,
identifierExpression.Parent is InvocationExpression,
IsInvoked(identifierExpression),
false, true);
if (rr != null && rr.IsValid)
return rr;
@ -590,7 +590,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -590,7 +590,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
if (c != null) {
ResolveResult rr = ResolveMember(c.DefaultReturnType, identifier,
identifierExpression.TypeArguments,
identifierExpression.Parent is InvocationExpression,
IsInvoked(identifierExpression),
false, null);
if (rr != null && rr.IsValid)
return rr;
@ -937,6 +937,16 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -937,6 +937,16 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return null;
}
if (v.ParentLambdaExpression != null && (v.TypeRef == null || v.TypeRef.IsNull)) {
IReturnType[] lambdaParameterTypes = GetImplicitLambdaParameterTypes(v.ParentLambdaExpression);
if (lambdaParameterTypes != null) {
int parameterIndex = v.ParentLambdaExpression.Parameters.FindIndex(p => p.ParameterName == v.Name);
if (parameterIndex >= 0 && parameterIndex < lambdaParameterTypes.Length) {
return lambdaParameterTypes[parameterIndex];
}
}
}
// Don't create multiple IReturnType's for the same local variable.
// This is required to ensure that the protection against infinite recursion
// for type inference cycles in InferredReturnType works correctly.
@ -946,9 +956,13 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -946,9 +956,13 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
return rt;
if (v.TypeRef == null || v.TypeRef.IsNull || v.TypeRef.Type == "var") {
rt = new InferredReturnType(v.Initializer, this);
if (v.IsLoopVariable) {
rt = new ElementReturnType(this.projectContent, rt);
if (v.ParentLambdaExpression != null) {
rt = new LambdaParameterReturnType(v.ParentLambdaExpression, v.Name, this);
} else {
rt = new InferredReturnType(v.Initializer, this);
if (v.IsLoopVariable) {
rt = new ElementReturnType(this.projectContent, rt);
}
}
} else {
rt = TypeVisitor.CreateReturnType(v.TypeRef, this);
@ -1146,6 +1160,73 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -1146,6 +1160,73 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
CtrlSpaceResolveHelper.AddImportedNamespaceContents(result, cu, callingClass);
}
sealed class CompareLambdaByLocation : IEqualityComparer<LambdaExpression>
{
public bool Equals(LambdaExpression x, LambdaExpression y)
{
return x.StartLocation == y.StartLocation && x.EndLocation == y.EndLocation;
}
public int GetHashCode(LambdaExpression obj)
{
return unchecked (8123351 * obj.StartLocation.GetHashCode() + obj.EndLocation.GetHashCode());
}
}
Dictionary<LambdaExpression, IReturnType[]> lambdaParameterTypes = new Dictionary<LambdaExpression, IReturnType[]>(new CompareLambdaByLocation());
internal void SetImplicitLambdaParameterTypes(LambdaExpression lambda, IReturnType[] types)
{
lambdaParameterTypes[lambda] = types;
}
internal void UnsetImplicitLambdaParameterTypes(LambdaExpression lambda)
{
lambdaParameterTypes.Remove(lambda);
}
IReturnType[] GetImplicitLambdaParameterTypes(LambdaExpression lambda)
{
IReturnType[] types;
if (lambdaParameterTypes.TryGetValue(lambda, out types))
return types;
else
return null;
}
internal static bool IsInvoked(Expression expr)
{
InvocationExpression ie = expr.Parent as InvocationExpression;
if (ie != null) {
return ie.TargetObject == expr;
}
return false;
}
public IReturnType GetExpectedTypeFromContext(Expression expr)
{
if (expr == null)
return null;
InvocationExpression ie = expr.Parent as InvocationExpression;
if (ie != null) {
int index = ie.Arguments.IndexOf(expr);
if (index < 0)
return null;
MemberResolveResult mrr = ResolveInternal(ie, ExpressionContext.Default) as MemberResolveResult;
if (mrr != null) {
if (mrr.IsExtensionMethodCall)
index++;
IMethod m = mrr.ResolvedMember as IMethod;
if (m != null && index < m.Parameters.Count)
return m.Parameters[index].ReturnType;
}
}
return null;
}
}
}

31
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs

@ -87,14 +87,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -87,14 +87,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
public override object VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, object data)
{
AnonymousMethodReturnType amrt = new AnonymousMethodReturnType(resolver.CompilationUnit);
if (anonymousMethodExpression.HasParameterList) {
amrt.MethodParameters = new List<IParameter>();
foreach (ParameterDeclarationExpression param in anonymousMethodExpression.Parameters) {
amrt.MethodParameters.Add(NRefactoryASTConvertVisitor.CreateParameter(param, resolver.CallingMember as IMethod, resolver.CallingClass, resolver.CompilationUnit));
}
}
return CreateResolveResult(amrt);
return CreateResolveResult(new LambdaReturnType(anonymousMethodExpression, resolver));
}
public override object VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, object data)
@ -281,25 +274,28 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -281,25 +274,28 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
IReturnType[] argumentTypes = invocationExpression.Arguments.Select<Expression, IReturnType>(ResolveType).ToArray();
IMethod firstResult = null;
MemberResolveResult firstResult = null;
foreach (MethodGroup methodGroup in mgrr.Methods) {
bool resultIsAcceptable;
IMethod result;
IMethod method;
if (methodGroup.IsExtensionMethodGroup) {
IReturnType[] extendedTypes = new IReturnType[argumentTypes.Length + 1];
extendedTypes[0] = mgrr.ContainingType;
argumentTypes.CopyTo(extendedTypes, 1);
result = MemberLookupHelper.FindOverload(methodGroup, extendedTypes, out resultIsAcceptable);
method = MemberLookupHelper.FindOverload(methodGroup, extendedTypes, out resultIsAcceptable);
} else {
result = MemberLookupHelper.FindOverload(methodGroup, argumentTypes, out resultIsAcceptable);
method = MemberLookupHelper.FindOverload(methodGroup, argumentTypes, out resultIsAcceptable);
}
MemberResolveResult result = CreateMemberResolveResult(method);
if (result != null && methodGroup.IsExtensionMethodGroup)
result.IsExtensionMethodCall = true;
if (resultIsAcceptable)
return CreateMemberResolveResult(result);
return result;
if (firstResult == null)
firstResult = result;
}
if (firstResult != null) {
return CreateMemberResolveResult(firstResult);
return firstResult;
} else {
return FallbackResolveMethod(invocationExpression, mgrr, argumentTypes);
}
@ -334,8 +330,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -334,8 +330,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
List<IMethod> methods = mgrr.ContainingType.GetMethods().Where(m => resolver.IsSameName(m.Name, mre.MemberName)).ToList();
bool resultIsAcceptable;
IMethod result = MemberLookupHelper.FindOverload(
methods,
argumentTypes, out resultIsAcceptable);
methods, argumentTypes, out resultIsAcceptable);
if (result != null) {
return CreateMemberResolveResult(result);
}
@ -347,7 +342,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -347,7 +342,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
public override object VisitLambdaExpression(LambdaExpression lambdaExpression, object data)
{
return null;
return CreateResolveResult(new LambdaReturnType(lambdaExpression, resolver));
}
public override object VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data)
@ -382,7 +377,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -382,7 +377,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
}
return resolver.ResolveMember(type, memberReferenceExpression.MemberName,
memberReferenceExpression.TypeArguments,
memberReferenceExpression.Parent is InvocationExpression,
NRefactoryResolver.IsInvoked(memberReferenceExpression),
typeRR == null, // allow extension methods only for non-static method calls
targetRR is BaseResolveResult ? (bool?)true : null // allow calling protected members using "base."
);

2
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/VBNetToCSharpConvertVisitor.cs

@ -385,7 +385,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver @@ -385,7 +385,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
{
if (rr is MethodGroupResolveResult
&& !(expression.Parent is AddressOfExpression)
&& !(expression.Parent is InvocationExpression))
&& !(NRefactoryResolver.IsInvoked(expression)))
{
InvocationExpression ie = new InvocationExpression(expression);
ReplaceCurrentNode(ie);

16
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs

@ -484,7 +484,19 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -484,7 +484,19 @@ namespace ICSharpCode.SharpDevelop.Dom
public override ResolveResult Clone()
{
return new MemberResolveResult(this.CallingClass, this.CallingMember, this.ResolvedMember);
return new MemberResolveResult(this.CallingClass, this.CallingMember, this.ResolvedMember) {
IsExtensionMethodCall = IsExtensionMethodCall
};
}
bool isExtensionMethodCall;
public bool IsExtensionMethodCall {
get { return isExtensionMethodCall; }
set {
CheckBeforeMutation();
isExtensionMethodCall = value;
}
}
/// <summary>
@ -642,6 +654,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -642,6 +654,7 @@ namespace ICSharpCode.SharpDevelop.Dom
throw new ArgumentNullException("name");
this.containingType = containingType;
this.name = name;
this.ResolvedType = new MethodGroupReturnType();
}
public MethodGroupResolveResult(IClass callingClass, IMember callingMember, IReturnType containingType, string name,
@ -657,6 +670,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -657,6 +670,7 @@ namespace ICSharpCode.SharpDevelop.Dom
this.containingType = containingType;
this.name = name;
this.possibleMethods = possibleMethods;
this.ResolvedType = new MethodGroupReturnType();
}
public override ResolveResult Clone()

Loading…
Cancel
Save