icsharpcode / CodeConverter

Convert code from C# to VB.NET and vice versa using Roslyn
https://icsharpcode.github.io/CodeConverter/
MIT License
826 stars 216 forks source link

VB -> C#: Query syntax -Linq with multiple groups #298

Open mrmonday opened 5 years ago

mrmonday commented 5 years ago

Input code

Public Class Class1
    Sub Foo()
        Dim xs As New List(Of String)
        Dim y = From x In xs Group By x.Length, x.Count() Into Group
    End Sub
End Class

Erroneous output

    public class Class1
    {
        public void Foo()
        {
            List<string> xs = new List<string>();
            ;/* Cannot convert LocalDeclarationStatementSyntax, System.InvalidOperationException: Sequence contains no elements
   at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
   at ICSharpCode.CodeConverter.CSharp.QueryConverter.ConvertSubQuery(FromClauseSyntax fromClauseSyntax, QueryClauseSyntax clauseEnd, QueryBodySyntax nestedClause, SyntaxList`1 convertedClauses)
   at ICSharpCode.CodeConverter.CSharp.QueryConverter.ConvertQuerySegments(IEnumerable`1 querySegments, FromClauseSyntax fromClauseSyntax)
   at ICSharpCode.CodeConverter.CSharp.QueryConverter.ConvertClauses(SyntaxList`1 clauses)
   at ICSharpCode.CodeConverter.CSharp.VisualBasicConverter.NodesVisitor.VisitQueryExpression(QueryExpressionSyntax node)
   at Microsoft.CodeAnalysis.VisualBasic.Syntax.QueryExpressionSyntax.Accept[TResult](VisualBasicSyntaxVisitor`1 visitor)
   at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxVisitor`1.Visit(SyntaxNode node)
   at ICSharpCode.CodeConverter.CSharp.CommentConvertingNodesVisitor.DefaultVisit(SyntaxNode node)
   at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxVisitor`1.VisitQueryExpression(QueryExpressionSyntax node)
   at Microsoft.CodeAnalysis.VisualBasic.Syntax.QueryExpressionSyntax.Accept[TResult](VisualBasicSyntaxVisitor`1 visitor)
   at ICSharpCode.CodeConverter.CSharp.CommonConversions.ConvertInitializer(VariableDeclaratorSyntax declarator)
   at ICSharpCode.CodeConverter.CSharp.CommonConversions.SplitVariableDeclarations(VariableDeclaratorSyntax declarator, Boolean preferExplicitType)
   at ICSharpCode.CodeConverter.CSharp.VisualBasicConverter.MethodBodyVisitor.VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
   at Microsoft.CodeAnalysis.VisualBasic.Syntax.LocalDeclarationStatementSyntax.Accept[TResult](VisualBasicSyntaxVisitor`1 visitor)
   at Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxVisitor`1.Visit(SyntaxNode node)
   at ICSharpCode.CodeConverter.CSharp.CommentConvertingMethodBodyVisitor.ConvertWithTrivia(SyntaxNode node)
   at ICSharpCode.CodeConverter.CSharp.CommentConvertingMethodBodyVisitor.DefaultVisit(SyntaxNode node)

Input: 
        Dim y = From x In xs Group By x.Length, x.Count() Into Group

 */
        }
    }

Expected output

    public class Class1
    {
        public void Foo()
        {
            List<string> xs = new List<string>();
            // Not 100% that this does the same thing.
            var y = from x in xs group x by new { x.Length, Count = x.Count(), Group = xs.AsEnumerable() };
        }
    }
GrahamTheCoder commented 5 years ago

I'll leave this open for now since its much more specific and achievable than #29 I'd slightly caution against working on this issue in isolation though. There's a collection of very custom code for the query syntax, but really, more careful investigation into how the vb and c# parser deal with it might yield a more general solution. The main problem I've had, is that I don't understand VB's query syntax. It's a whole language of its own that appears more expressive than C#s version. If we can find a way to turn it into linq method chaining in vb, then convert that, it'd be easier. Either the compiler, or some other library may assist. Checking what sort of il gets generated would be good start

GrahamTheCoder commented 4 years ago

I've stopped this throwing an error, but not gone as far as completely fixing it (it'll still be a compile error from the consuming code). The output of the LinqGroupByTwoThingsAnonymously test is now:

using System.Collections.Generic;
using System.Linq;

public partial class Class1
{
    public void Foo()
    {
        var xs = new List<string>();
        var y = from x in xs
                group x by new { x.Length, Count = x.Count() };
    }
}

but I think should be

using System.Collections.Generic;
using System.Linq;

public partial class Class1
{
    public void Foo()
    {
        var xs = new List<string>();
        var y = from x in xs
                group x by new { x.Length, Count = x.Count() } into g
                select new { Length = g.Key.Length, Count = g.Key.Count, Group = g.AsEnumerable() };
    }
}
jrmoreno1 commented 2 years ago

Exact opposite error message (no elements versus more than one), but it might be related.

Cannot convert QueryExpressionSyntax, System.InvalidOperationException: Sequence contains more than one element at System.Linq.Enumerable.Single[TSource](IEnumerable1 source) at ICSharpCode.CodeConverter.CSharp.QueryConverter.<ConvertSubQueryAsync>d__15.MoveNext() --- End of stack trace from previous location where exception was thrown --- at ICSharpCode.CodeConverter.CSharp.QueryConverter.<ConvertQueryWithContinuationAsync>d__14.MoveNext() --- End of stack trace from previous location where exception was thrown --- at ICSharpCode.CodeConverter.CSharp.QueryConverter.<ConvertQueryWithContinuationsAsync>d__13.MoveNext() --- End of stack trace from previous location where exception was thrown --- at ICSharpCode.CodeConverter.CSharp.QueryConverter.<ConvertQuerySegmentsAsync>d__11.MoveNext() --- End of stack trace from previous location where exception was thrown --- at ICSharpCode.CodeConverter.CSharp.QueryConverter.<ConvertClausesAsync>d__6.MoveNext() --- End of stack trace from previous location where exception was thrown --- at ICSharpCode.CodeConverter.CSharp.ExpressionNodeVisitor.<VisitQueryExpression>d__70.MoveNext() --- End of stack trace from previous location where exception was thrown --- at ICSharpCode.CodeConverter.CSharp.CommentConvertingVisitorWrapper.<ConvertHandledAsync>d__81.MoveNext()

Dim gridDataSource = (From p In _Parent Join pt In _ParentType On pt.parentId Equals p.parentId Group p By key = p.parentId, desc = p.ParentTypeDesc, sequence = pt.Sequence Into Group Select New With {.parentId = key, .ParentTypeDesc = desc, .Sequence = sequence}).OrderBy(Function(c) c.Sequence)