Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VB -> C#: Improve query syntax support #129

Merged
merged 14 commits into from
Aug 20, 2018
5 changes: 3 additions & 2 deletions ICSharpCode.CodeConverter/CSharp/CommonConversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using TypeSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax;
using VariableDeclaratorSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.VariableDeclaratorSyntax;
using VisualBasicExtensions = Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions;
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;

namespace ICSharpCode.CodeConverter.CSharp
{
Expand Down Expand Up @@ -346,7 +347,7 @@ private SyntaxToken VisualBasicDefaultVisibility(TokenContext context, bool isVa
}

internal SyntaxList<ArrayRankSpecifierSyntax> ConvertArrayRankSpecifierSyntaxes(
SyntaxList<Microsoft.CodeAnalysis.VisualBasic.Syntax.ArrayRankSpecifierSyntax> arrayRankSpecifierSyntaxs,
SyntaxList<VBSyntax.ArrayRankSpecifierSyntax> arrayRankSpecifierSyntaxs,
ArgumentListSyntax nodeArrayBounds, bool withSizes = true)
{
var bounds = SyntaxFactory.List(arrayRankSpecifierSyntaxs.Select(r => (ArrayRankSpecifierSyntax)r.Accept(_nodesVisitor)));
Expand All @@ -372,7 +373,7 @@ public IEnumerable<ExpressionSyntax> ConvertArrayBounds(ArgumentListSyntax argum
return argumentListSyntax.Arguments.Select(a => IncreaseArrayUpperBoundExpression(((SimpleArgumentSyntax)a).Expression));
}

private ExpressionSyntax IncreaseArrayUpperBoundExpression(Microsoft.CodeAnalysis.VisualBasic.Syntax.ExpressionSyntax expr)
private ExpressionSyntax IncreaseArrayUpperBoundExpression(VBSyntax.ExpressionSyntax expr)
{
var constant = _semanticModel.GetConstantValue(expr);
if (constant.HasValue && constant.Value is int)
Expand Down
128 changes: 4 additions & 124 deletions ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using ICSharpCode.CodeConverter.Shared;
using ICSharpCode.CodeConverter.Util;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using ISymbolExtensions = ICSharpCode.CodeConverter.Util.ISymbolExtensions;
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
using VBasic = Microsoft.CodeAnalysis.VisualBasic;
using static ICSharpCode.CodeConverter.CSharp.SyntaxKindExtensions;
using SyntaxExtensions = ICSharpCode.CodeConverter.Util.SyntaxExtensions;
using SyntaxNodeExtensions = ICSharpCode.CodeConverter.Util.SyntaxNodeExtensions;
using SyntaxToken = Microsoft.CodeAnalysis.SyntaxToken;

namespace ICSharpCode.CodeConverter.CSharp
Expand All @@ -30,6 +24,7 @@ class NodesVisitor : VBasic.VisualBasicSyntaxVisitor<CSharpSyntaxNode>
private static readonly SyntaxToken SemicolonToken = SyntaxFactory.Token(SyntaxKind.SemicolonToken);
private static readonly TypeSyntax VarType = SyntaxFactory.ParseTypeName("var");
private readonly AdditionalInitializers _additionalInitializers;
private readonly QueryConverter _queryConverter;
public CommentConvertingNodesVisitor TriviaConvertingVisitor { get; }

private CommonConversions CommonConversions { get; }
Expand All @@ -40,6 +35,7 @@ public NodesVisitor(SemanticModel semanticModel)
TriviaConvertingVisitor = new CommentConvertingNodesVisitor(this);
_createConvertMethodsLookupByReturnType = CreateConvertMethodsLookupByReturnType(semanticModel);
CommonConversions = new CommonConversions(semanticModel, TriviaConvertingVisitor);
_queryConverter = new QueryConverter(CommonConversions, TriviaConvertingVisitor);
_additionalInitializers = new AdditionalInitializers();
}

Expand Down Expand Up @@ -1210,102 +1206,14 @@ public override CSharpSyntaxNode VisitCollectionInitializer(VBSyntax.CollectionI

public override CSharpSyntaxNode VisitQueryExpression(VBSyntax.QueryExpressionSyntax node)
{
var vbBodyClauses = node.Clauses;
var vbFromClause = vbBodyClauses.OfType<VBSyntax.FromClauseSyntax>().First();
var fromClauseSyntax = ConvertFromClauseSyntax(vbFromClause);
var vbGroupClause = vbBodyClauses.OfType<VBSyntax.GroupByClauseSyntax>().SingleOrDefault();
var vbSelectClause = vbBodyClauses.OfType<VBSyntax.SelectClauseSyntax>().SingleOrDefault();
var selectClauseSyntax = vbSelectClause != null
? ConvertSelectClauseSyntax(vbSelectClause)
: CreateDefaultSelectClause(fromClauseSyntax);
var alreadyConverted = new VBSyntax.QueryClauseSyntax[] { vbFromClause, vbGroupClause, vbSelectClause };
SelectOrGroupClauseSyntax selectOrGroup = null;
QueryContinuationSyntax queryContinuation = null;
var queryClauseSyntaxs = SyntaxFactory.List(vbBodyClauses.TakeWhile(x => x != vbGroupClause).Except(alreadyConverted).Select(ConvertQueryBodyClause));
var continuedClauses = SyntaxFactory.List(vbBodyClauses.SkipWhile(x => x != vbGroupClause).Skip(1).Except(alreadyConverted).Select(ConvertQueryBodyClause));
if (vbGroupClause != null) {
selectOrGroup = ConvertGroupByClause(vbGroupClause);
queryContinuation = SyntaxFactory.QueryContinuation(GetGroupIdentifier(vbGroupClause),
SyntaxFactory.QueryBody(continuedClauses, selectClauseSyntax, null));
} else {
selectOrGroup = selectClauseSyntax;
}

var queryBodySyntax = SyntaxFactory.QueryBody(queryClauseSyntaxs, selectOrGroup, queryContinuation);
return SyntaxFactory.QueryExpression(fromClauseSyntax, queryBodySyntax);
}

private FromClauseSyntax ConvertFromClauseSyntax(VBSyntax.FromClauseSyntax vbFromClause)
{
var collectionRangeVariableSyntax = vbFromClause.Variables.Single();
var fromClauseSyntax = SyntaxFactory.FromClause(
ConvertIdentifier(collectionRangeVariableSyntax.Identifier.Identifier),
(ExpressionSyntax) collectionRangeVariableSyntax.Expression.Accept(TriviaConvertingVisitor));
return fromClauseSyntax;
}

private SelectClauseSyntax ConvertSelectClauseSyntax(VBSyntax.SelectClauseSyntax vbFromClause)
{
var collectionRangeVariableSyntax = vbFromClause.Variables.Single();
return SyntaxFactory.SelectClause(
(ExpressionSyntax)collectionRangeVariableSyntax.Expression.Accept(TriviaConvertingVisitor)
);
}

private static SelectClauseSyntax CreateDefaultSelectClause(FromClauseSyntax fromClauseSyntax)
{
return SyntaxFactory.SelectClause(SyntaxFactory.IdentifierName(fromClauseSyntax.Identifier));
}

private QueryClauseSyntax ConvertQueryBodyClause(VBSyntax.QueryClauseSyntax node)
{
return node.TypeSwitch<VBSyntax.QueryClauseSyntax, VBSyntax.FromClauseSyntax, VBSyntax.JoinClauseSyntax, VBSyntax.LetClauseSyntax, VBSyntax.OrderByClauseSyntax, VBSyntax.WhereClauseSyntax, QueryClauseSyntax>(
//(VBSyntax.AggregateClauseSyntax ags) => null,
//(VBSyntax.DistinctClauseSyntax ds) => null,
ConvertFromClauseSyntax,
ConvertJoinClause,
ConvertLetClause,
ConvertOrderByClause,
//(VBSyntax.PartitionClauseSyntax ps) => null, // TODO Convert to Skip and Take methods (they don't exist in C#s query syntax)
//(VBSyntax.PartitionWhileClauseSyntax pws) => null, // TODO Convert to SkipWhile and TakeWhile methods (they don't exist in C#s query syntax)
ConvertWhereClause,
_ => throw new NotImplementedException($"Conversion for query clause with kind '{node.Kind()}' not implemented"));
}

private GroupClauseSyntax ConvertGroupByClause(VBSyntax.GroupByClauseSyntax gs)
{
if (gs == null) return null;
var item = (IdentifierNameSyntax) gs.Items.Single().Expression.Accept(TriviaConvertingVisitor);
var keyExpression = (ExpressionSyntax) gs.Keys.Single().Expression.Accept(TriviaConvertingVisitor);
return SyntaxFactory.GroupClause(item, keyExpression);
}

private SyntaxToken GetGroupIdentifier(VBSyntax.GroupByClauseSyntax gs)
{
return ConvertIdentifier(gs.AggregationVariables.Select(x => x.Aggregation).OfType<VBSyntax.FunctionAggregationSyntax>().First().FunctionName);
return _queryConverter.ConvertClauses(node.Clauses);
}

private SyntaxToken ConvertIdentifier(SyntaxToken identifierIdentifier, bool isAttribute = false)
{
return CommonConversions.ConvertIdentifier(identifierIdentifier, isAttribute);
}

private QueryClauseSyntax ConvertWhereClause(VBSyntax.WhereClauseSyntax ws)
{
return SyntaxFactory.WhereClause((ExpressionSyntax) ws.Condition.Accept(TriviaConvertingVisitor));
}

private QueryClauseSyntax ConvertLetClause(VBSyntax.LetClauseSyntax ls)
{
var singleVariable = ls.Variables.Single();
return SyntaxFactory.LetClause(ConvertIdentifier(singleVariable.NameEquals.Identifier.Identifier), (ExpressionSyntax) singleVariable.Expression.Accept(TriviaConvertingVisitor));
}

private QueryClauseSyntax ConvertOrderByClause(VBSyntax.OrderByClauseSyntax os)
{
return SyntaxFactory.OrderByClause(SyntaxFactory.SeparatedList(os.Orderings.Select(o => (OrderingSyntax) o.Accept(TriviaConvertingVisitor))));
}

public override CSharpSyntaxNode VisitOrdering(VBSyntax.OrderingSyntax node)
{
var convertToken = node.Kind().ConvertToken();
Expand All @@ -1314,34 +1222,6 @@ public override CSharpSyntaxNode VisitOrdering(VBSyntax.OrderingSyntax node)
return SyntaxFactory.Ordering(convertToken, expressionSyntax, ascendingOrDescendingKeyword);
}

private QueryClauseSyntax ConvertJoinClause(VBSyntax.JoinClauseSyntax js)
{
var variable = js.JoinedVariables.Single();
var joinLhs = SingleExpression(js.JoinConditions.Select(c => c.Left.Accept(TriviaConvertingVisitor))
.Cast<ExpressionSyntax>().ToList());
var joinRhs = SingleExpression(js.JoinConditions.Select(c => c.Right.Accept(TriviaConvertingVisitor))
.Cast<ExpressionSyntax>().ToList());
var convertIdentifier = ConvertIdentifier(variable.Identifier.Identifier);
var expressionSyntax = (ExpressionSyntax) variable.Expression.Accept(TriviaConvertingVisitor);

JoinIntoClauseSyntax joinIntoClauseSyntax = null;
if (js is VBSyntax.GroupJoinClauseSyntax gjs) {
joinIntoClauseSyntax = gjs.AggregationVariables
.Where(a => a.Aggregation is VBSyntax.GroupAggregationSyntax)
.Select(a => SyntaxFactory.JoinIntoClause(ConvertIdentifier(a.NameEquals.Identifier.Identifier)))
.SingleOrDefault();
}
return SyntaxFactory.JoinClause(null, convertIdentifier, expressionSyntax, joinLhs, joinRhs, joinIntoClauseSyntax);
}

private ExpressionSyntax SingleExpression(IReadOnlyCollection<ExpressionSyntax> expressions)
{
if (expressions.Count == 1) return expressions.Single();
return SyntaxFactory.AnonymousObjectCreationExpression(SyntaxFactory.SeparatedList(expressions.Select((e, i) =>
SyntaxFactory.AnonymousObjectMemberDeclarator(SyntaxFactory.NameEquals($"key{i}"), e)
)));
}

public override CSharpSyntaxNode VisitNamedFieldInitializer(VBSyntax.NamedFieldInitializerSyntax node)
{
if (node?.Parent?.Parent is VBSyntax.AnonymousObjectCreationExpressionSyntax) {
Expand Down Expand Up @@ -1646,7 +1526,7 @@ private bool EnclosingTypeImplicitlyQualifiesSymbol(SyntaxNode syntaxNodeContext
{
ISymbol typeContext = syntaxNodeContext.GetEnclosingDeclaredTypeSymbol(_semanticModel);
var implicitCsQualifications = ((ITypeSymbol) typeContext).GetBaseTypesAndThis()
.Concat(CSharpUtil.FollowProperty(typeContext, n => n.ContainingSymbol))
.Concat(typeContext.FollowProperty(n => n.ContainingSymbol))
.ToList();

return implicitCsQualifications.Contains(symbolToCheck);
Expand Down
Loading