From 0f2b0c380e9f8e2447613b18341aad24be23d38d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 14 Feb 2012 10:56:41 +0100 Subject: [PATCH] Fixed lambda type inference in delegate creation expressions. ("new Func(a => a)") --- .../Expressions/ParenthesizedExpression.cs | 23 ++++++++++++++++- .../Resolver/FindReferencedEntities.cs | 2 +- .../Resolver/FindReferences.cs | 2 +- .../Resolver/ResolveVisitor.cs | 25 +++++++------------ .../CSharp/Resolver/LambdaTests.cs | 25 +++++++++++++++++++ .../Implementation/SimpleCompilation.cs | 2 +- 6 files changed, 59 insertions(+), 20 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs index 8c32a42ddf..596b6f9684 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/ParenthesizedExpression.cs @@ -1,6 +1,6 @@ // // ParenthesizedExpression.cs -// +// // Author: // Mike Krüger // @@ -63,5 +63,26 @@ namespace ICSharpCode.NRefactory.CSharp ParenthesizedExpression o = other as ParenthesizedExpression; return o != null && this.Expression.DoMatch(o.Expression, match); } + + /// + /// Gets whether the expression acts like a parenthesized expression, + /// i.e. whether information about the expected type (for lambda type inference) flows + /// into the inner expression. + /// + /// Returns true for ParenthesizedExpression, CheckedExpression or UncheckedExpression; false otherwise. + public static bool ActsAsParenthesizedExpression(AstNode expression) + { + return expression is ParenthesizedExpression || expression is CheckedExpression || expression is UncheckedExpression; + } + + /// + /// Unpacks the given expression if it is a ParenthesizedExpression, CheckedExpression or UncheckedExpression. + /// + public static Expression UnpackParenthesizedExpression(Expression expr) + { + while (ActsAsParenthesizedExpression(expr)) + expr = expr.GetChildByRole(ParenthesizedExpression.Roles.Expression); + return expr; + } } } diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/FindReferencedEntities.cs b/ICSharpCode.NRefactory.CSharp/Resolver/FindReferencedEntities.cs index 38846458c5..9382ea50a7 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/FindReferencedEntities.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/FindReferencedEntities.cs @@ -43,7 +43,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public void Resolved(AstNode node, ResolveResult result) { - if (ResolveVisitor.ActsAsParenthesizedExpression(node)) + if (ParenthesizedExpression.ActsAsParenthesizedExpression(node)) return; MemberResolveResult mrr = result as MemberResolveResult; diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs b/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs index 42fe828b26..6b94262e60 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/FindReferences.cs @@ -617,7 +617,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver InvocationExpression ie = node as InvocationExpression; if (ie != null) { - Expression target = ResolveVisitor.UnpackParenthesizedExpression(ie.Target); + Expression target = ParenthesizedExpression.UnpackParenthesizedExpression(ie.Target); IdentifierExpression ident = target as IdentifierExpression; if (ident != null) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs index d9ad1a87c1..b4d91537dc 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveVisitor.cs @@ -517,7 +517,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public ConversionWithTargetType GetConversionWithTargetType(Expression expr) { - MergeUndecidedLambdas(); + GetResolverStateBefore(expr); ResolveParentForConversion(expr); ConversionWithTargetType result; if (conversionDict.TryGetValue(expr, out result)) { @@ -1305,6 +1305,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ResolveResult[] arguments = GetArguments(objectCreateExpression.Arguments, out argumentNames); ResolveResult rr = resolver.ResolveObjectCreation(type, arguments, argumentNames); + if (arguments.Length == 1) { + // process conversion in case it's a delegate creation + ProcessConversionResult(objectCreateExpression.Arguments.Single(), rr as ConversionResolveResult); + } + // process conversions in all other cases ProcessConversionsInInvocation(null, objectCreateExpression.Arguments, rr as CSharpInvocationResolveResult); return rr; } else { @@ -2233,7 +2238,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver AstNode parent = expression.Parent; // Continue going upwards until we find a node that can be resolved and provides // an expected type. - while (ActsAsParenthesizedExpression(parent) || parent is NamedArgumentExpression || parent is ArrayInitializerExpression) { + while (ParenthesizedExpression.ActsAsParenthesizedExpression(parent) || CSharpAstResolver.IsUnresolvableNode(parent)) { parent = parent.Parent; } CSharpResolver storedResolver; @@ -2243,21 +2248,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ResetContext(storedResolver, delegate { Resolve(parent); }); Log.Unindent(); } else { - Log.WriteLine("Could not find a suitable parent for '" + expression); + Log.WriteLine("Could not find a suitable parent for '" + expression + "'"); } } - - internal static bool ActsAsParenthesizedExpression(AstNode expression) - { - return expression is ParenthesizedExpression || expression is CheckedExpression || expression is UncheckedExpression; - } - - internal static Expression UnpackParenthesizedExpression(Expression expr) - { - while (ActsAsParenthesizedExpression(expr)) - expr = expr.GetChildByRole(ParenthesizedExpression.Roles.Expression); - return expr; - } #endregion #region AnalyzeLambda @@ -3340,7 +3333,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver QueryExpression query = querySelectClause.Parent as QueryExpression; string rangeVariable = GetSingleRangeVariable(query); if (rangeVariable != null) { - IdentifierExpression ident = UnpackParenthesizedExpression(querySelectClause.Expression) as IdentifierExpression; + IdentifierExpression ident = ParenthesizedExpression.UnpackParenthesizedExpression(querySelectClause.Expression) as IdentifierExpression; if (ident != null && ident.Identifier == rangeVariable && !ident.TypeArguments.Any()) { // selecting the single identifier that is the range variable if (query.Clauses.Count > 2) { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs index aa80fc4977..7ba7a5f597 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/LambdaTests.cs @@ -19,6 +19,7 @@ using System; using System.Linq; using ICSharpCode.NRefactory.Semantics; +using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; using NUnit.Framework; @@ -421,6 +422,30 @@ class TestClass { Assert.IsTrue(c.IsValid); } + [Test] + public void ImplicitLambdaInNewFunc() + { + string program = @"using System; +class Test { + static bool b; + object x = new Func(a => $a$.ToString()); +}"; + var r = Resolve(program); + Assert.AreEqual("System.Int32", r.Type.ReflectionName); + } + + [Test] + public void LambdaInNewAction() + { + string program = @"using System; +class Test { + static bool b; + object x = new Action(() => $b = true$); +}"; + var c = GetConversion(program); + Assert.IsTrue(c.IsValid); + } + [Test] public void RaisePropertyChanged_WithExpressionLambda() { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleCompilation.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleCompilation.cs index d44cb8e8f5..ad09884672 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleCompilation.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/SimpleCompilation.cs @@ -66,7 +66,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation List referencedAssemblies = new List(); foreach (var asmRef in assemblyReferences) { IAssembly asm = asmRef.Resolve(context); - if (asm != null) + if (asm != null && !referencedAssemblies.Contains(asm)) referencedAssemblies.Add(asm); } this.referencedAssemblies = referencedAssemblies.AsReadOnly();