CodeConverter icon indicating copy to clipboard operation
CodeConverter copied to clipboard

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

Open mrmonday opened this issue 5 years ago • 3 comments

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() };
        }
    }

mrmonday avatar Apr 22 '19 12:04 mrmonday

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 avatar Apr 24 '19 22:04 GrahamTheCoder

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() };
    }
}

GrahamTheCoder avatar Jan 19 '20 18:01 GrahamTheCoder

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)

jrmoreno1 avatar Mar 11 '22 13:03 jrmoreno1