diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index a8d4adeb8..f40d5d154 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -714,21 +714,40 @@ namespace ICSharpCode.Decompiler.Ast return ace; } } - var oce = new Ast.ObjectCreateExpression(); if (declaringType.IsAnonymousType()) { MethodDefinition ctor = ((MethodReference)operand).Resolve(); if (methodDef != null) { - oce.Initializer = new ArrayInitializerExpression(); + AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression(); + bool allNamesCanBeInferred = true; for (int i = 0; i < args.Count; i++) { - oce.Initializer.Elements.Add( - new NamedExpression { - Identifier = ctor.Parameters[i].Name, - Expression = args[i] - }); + string inferredName; + if (args[i] is IdentifierExpression) + inferredName = ((IdentifierExpression)args[i]).Identifier; + else if (args[i] is MemberReferenceExpression) + inferredName = ((MemberReferenceExpression)args[i]).MemberName; + else + inferredName = null; + + if (inferredName != ctor.Parameters[i].Name) { + allNamesCanBeInferred = false; + break; + } } + if (allNamesCanBeInferred) { + atce.Initializers.AddRange(args); + } else { + for (int i = 0; i < args.Count; i++) { + atce.Initializers.Add( + new NamedExpression { + Identifier = ctor.Parameters[i].Name, + Expression = args[i] + }); + } + } + return atce; } - return oce; } + var oce = new Ast.ObjectCreateExpression(); oce.Type = AstBuilder.ConvertType(declaringType); oce.Arguments.AddRange(args); return oce.WithAnnotation(operand); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs b/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs index 9c7c2cbf4..db3a79a22 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs @@ -78,11 +78,17 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause { - Expression = new ObjectCreateExpression { - Initializer = new ArrayInitializerExpression { - Elements = { + Expression = new Choice { + new AnonymousTypeCreateExpression { + Initializers = { new NamedNode("nae1", new NamedExpression { Expression = new IdentifierExpression() }), - new NamedNode("nae2", new NamedExpression { Expression = new AnyNode() }) + new NamedNode("nae2", new NamedExpression { Expression = new AnyNode("nae2Expr") }) + } + }, + new AnonymousTypeCreateExpression { + Initializers = { + new NamedNode("identifier", new IdentifierExpression()), + new AnyNode("nae2Expr") } } }}; @@ -100,12 +106,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (!match.Success) return false; QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last(); - NamedExpression nae1 = match.Get("nae1").Single(); - NamedExpression nae2 = match.Get("nae2").Single(); - if (nae1.Identifier != ((IdentifierExpression)nae1.Expression).Identifier) + NamedExpression nae1 = match.Get("nae1").SingleOrDefault(); + NamedExpression nae2 = match.Get("nae2").SingleOrDefault(); + if (nae1 != null && nae1.Identifier != ((IdentifierExpression)nae1.Expression).Identifier) return false; - IdentifierExpression nae2IdentExpr = nae2.Expression as IdentifierExpression; - if (nae2IdentExpr != null && nae2.Identifier == nae2IdentExpr.Identifier) { + Expression nae2Expr = match.Get("nae2Expr").Single(); + IdentifierExpression nae2IdentExpr = nae2Expr as IdentifierExpression; + if (nae2IdentExpr != null && (nae2 == null || nae2.Identifier == nae2IdentExpr.Identifier)) { // from * in (from x in ... select new { x = x, y = y }) ... // => // from x in ... ... @@ -127,7 +134,16 @@ namespace ICSharpCode.Decompiler.Ast.Transforms foreach (var clause in innerQuery.Clauses) { query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach()); } - query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = nae2.Identifier, Expression = nae2.Expression.Detach() }); + string ident; + if (nae2 != null) + ident = nae2.Identifier; + else if (nae2Expr is IdentifierExpression) + ident = ((IdentifierExpression)nae2Expr).Identifier; + else if (nae2Expr is MemberReferenceExpression) + ident = ((MemberReferenceExpression)nae2Expr).MemberName; + else + throw new InvalidOperationException("Could not infer name from initializer in AnonymousTypeCreateExpression"); + query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = ident, Expression = nae2Expr.Detach() }); } return true; } diff --git a/ICSharpCode.Decompiler/Tests/QueryExpressions.cs b/ICSharpCode.Decompiler/Tests/QueryExpressions.cs index 143f3d30c..28d80b4d6 100644 --- a/ICSharpCode.Decompiler/Tests/QueryExpressions.cs +++ b/ICSharpCode.Decompiler/Tests/QueryExpressions.cs @@ -35,7 +35,7 @@ public class QueryExpressions { public int OrderID; public DateTime OrderDate; - public Customer Customer; + public QueryExpressions.Customer Customer; public int CustomerID; public decimal Total; public IEnumerable Details; @@ -54,7 +54,7 @@ public class QueryExpressions { return from c in this.customers - where c.Orders.Count() > 10 + where c.Orders.Count() > 10 where c.Country == "DE" select c; } @@ -64,7 +64,12 @@ public class QueryExpressions return from c in this.customers from o in c.Orders - select new { c.Name, o.OrderID, o.Total }; + select new + { + c.Name, + o.OrderID, + o.Total + }; } public object SelectManyFollowedByOrderBy() @@ -73,7 +78,12 @@ public class QueryExpressions from c in this.customers from o in c.Orders orderby o.Total descending - select new { c.Name, o.OrderID, o.Total }; + select new + { + c.Name, + o.OrderID, + o.Total + }; } public object MultipleSelectManyFollowedBySelect() @@ -82,7 +92,12 @@ public class QueryExpressions from c in this.customers from o in c.Orders from d in o.Details - select new { c.Name, o.OrderID, d.Quantity }; + select new + { + c.Name, + o.OrderID, + d.Quantity + }; } public object MultipleSelectManyFollowedByLet() @@ -92,16 +107,25 @@ public class QueryExpressions from o in c.Orders from d in o.Details let x = d.Quantity * d.UnitPrice - select new { c.Name, o.OrderID, x }; + select new + { + c.Name, + o.OrderID, + x + }; } public object FromLetWhereSelect() { return from o in this.orders - let t = o.Details.Sum(d => d.UnitPrice * d.Quantity) - where t >= 1000 - select new { o.OrderID, Total = t }; + let t = o.Details.Sum((QueryExpressions.OrderDetail d) => d.UnitPrice * d.Quantity) + where t >= 1000m + select new + { + OrderID = o.OrderID, + Total = t + }; } public object MultipleLet() @@ -116,25 +140,34 @@ public class QueryExpressions public object Join() { return - from c in customers - join o in orders on c.CustomerID equals o.CustomerID - select new { c.Name, o.OrderDate, o.Total }; + from c in this.customers + join o in this.orders on c.CustomerID equals o.CustomerID + select new + { + c.Name, + o.OrderDate, + o.Total + }; } public object JoinInto() { return - from c in customers - join o in orders on c.CustomerID equals o.CustomerID into co - let n = co.Count() + from c in this.customers + join o in this.orders on c.CustomerID equals o.CustomerID into co + let n = co.Count() where n >= 10 - select new { c.Name, OrderCount = n }; + select new + { + Name = c.Name, + OrderCount = n + }; } public object OrderBy() { return - from o in orders + from o in this.orders orderby o.Customer.Name, o.Total descending select o; } @@ -142,14 +175,14 @@ public class QueryExpressions public object GroupBy() { return - from c in customers + from c in this.customers group c.Name by c.Country; } public object ExplicitType() { return - from Customer c in customers + from QueryExpressions.Customer c in this.customers where c.City == "London" select c; } @@ -157,8 +190,12 @@ public class QueryExpressions public object QueryContinuation() { return - from c in customers + from c in this.customers group c by c.Country into g - select new { Country = g.Key, CustCount = g.Count() }; + select new + { + Country = g.Key, + CustCount = g.Count() + }; } } diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index bd7c1d574..4d278e0e9 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.Tests TestFile(@"..\..\Tests\PropertiesAndEvents.cs"); } - [Test, Ignore("Formatting differences in anonymous method create expressions")] + [Test] public void QueryExpressions() { TestFile(@"..\..\Tests\QueryExpressions.cs");