Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions CodeConverter/CSharp/ExpressionNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal partial class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<T
private readonly HashSet<string> _extraUsingDirectives;
private readonly IOperatorConverter _operatorConverter;
private readonly Stack<ExpressionSyntax> _withBlockLhs = new();
private readonly Dictionary<VBSyntax.ConditionalAccessExpressionSyntax, ExpressionSyntax> _conditionalAccessReceivers = new();
private readonly ITypeContext _typeContext;
private readonly QueryConverter _queryConverter;
private readonly LambdaConverter _lambdaConverter;
Expand All @@ -44,8 +45,8 @@ public ExpressionNodeVisitor(SemanticModel semanticModel,
_typeContext = typeContext;
_extraUsingDirectives = extraUsingDirectives;
_argumentConverter = new ArgumentConverter(visualBasicEqualityComparison, typeContext, semanticModel, commonConversions);
_xmlExpressionConverter = new XmlExpressionConverter(xmlImportContext, extraUsingDirectives, TriviaConvertingExpressionVisitor);
_nameExpressionNodeVisitor = new NameExpressionNodeVisitor(semanticModel, _generatedNames, typeContext, extraUsingDirectives, _tempNameForAnonymousScope, _withBlockLhs, commonConversions, _argumentConverter, TriviaConvertingExpressionVisitor);
_xmlExpressionConverter = new XmlExpressionConverter(xmlImportContext, extraUsingDirectives, commonConversions, semanticModel, _withBlockLhs, _generatedNames, _conditionalAccessReceivers);
_nameExpressionNodeVisitor = new NameExpressionNodeVisitor(semanticModel, _generatedNames, typeContext, extraUsingDirectives, _tempNameForAnonymousScope, _withBlockLhs, commonConversions, _argumentConverter, TriviaConvertingExpressionVisitor, _conditionalAccessReceivers);
_operatorConverter = VbOperatorConversion.Create(TriviaConvertingExpressionVisitor, semanticModel, visualBasicEqualityComparison, commonConversions.TypeConversionAnalyzer);
_binaryExpressionConverter = new BinaryExpressionConverter(semanticModel, _operatorConverter, visualBasicEqualityComparison, visualBasicNullableTypesConverter, commonConversions);
}
Expand Down Expand Up @@ -212,6 +213,27 @@ public override async Task<CSharpSyntaxNode> VisitParenthesizedExpression(VBasic
public override async Task<CSharpSyntaxNode> VisitConditionalAccessExpression(VBasic.Syntax.ConditionalAccessExpressionSyntax node)
{
var leftExpression = await node.Expression.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor) ?? _withBlockLhs.Peek();
if (node.WhenNotNull.DescendantNodesAndSelf().OfType<VBSyntax.XmlMemberAccessExpressionSyntax>()
.Any(axis => axis.Token2.Text == "@" && axis.Ancestors().TakeWhile(NameExpressionNodeVisitor.IsMemberAccessChain).Contains(node))) {
// Attribute reads need a cast or a sequence null guard, neither of which can wrap a member binding.
var receiver = ValidSyntaxFactory.IdentifierName(NameGenerator.CS.GetUniqueVariableNameInScope(_semanticModel, _generatedNames, node, "xmlReceiver"));
_conditionalAccessReceivers.Add(node, receiver);
try {
var whenNotNull = await node.WhenNotNull.AcceptAsync<CSharpSyntaxNode>(TriviaConvertingExpressionVisitor);
var condition = CS.SyntaxFactory.IsPatternExpression(leftExpression,
CS.SyntaxFactory.RecursivePattern().WithPropertyPatternClause(CS.SyntaxFactory.PropertyPatternClause())
.WithDesignation(CS.SyntaxFactory.SingleVariableDesignation(receiver.Identifier)));
if (whenNotNull is StatementSyntax || node.Parent is VBSyntax.ExpressionStatementSyntax ||
_semanticModel.GetTypeInfo(node).Type?.SpecialType == SpecialType.System_Void) {
return CS.SyntaxFactory.IfStatement(condition, CS.SyntaxFactory.Block(
whenNotNull as StatementSyntax ?? CS.SyntaxFactory.ExpressionStatement((ExpressionSyntax)whenNotNull)));
}
return CS.SyntaxFactory.ParenthesizedExpression(CS.SyntaxFactory.ConditionalExpression(condition, (ExpressionSyntax)whenNotNull,
CS.SyntaxFactory.DefaultExpression(CommonConversions.GetTypeSyntax(_semanticModel.GetTypeInfo(node).Type))));
} finally {
_conditionalAccessReceivers.Remove(node);
}
}
return CS.SyntaxFactory.ConditionalAccessExpression(leftExpression, await node.WhenNotNull.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor));
}

Expand Down
10 changes: 6 additions & 4 deletions CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public override async Task<SyntaxList<StatementSyntax>> VisitExpressionStatement
return new SyntaxList<StatementSyntax>();
}

return SingleStatement(await node.Expression.AcceptAsync<ExpressionSyntax>(_expressionVisitor));
return SingleStatement(await node.Expression.AcceptAsync<CSharpSyntaxNode>(_expressionVisitor));
}

public override async Task<SyntaxList<StatementSyntax>> VisitAssignmentStatement(VBSyntax.AssignmentStatementSyntax node)
Expand Down Expand Up @@ -1224,16 +1224,18 @@ await stmt.Condition.AcceptAsync<ExpressionSyntax>(_expressionVisitor),

public override async Task<SyntaxList<StatementSyntax>> VisitCallStatement(VBSyntax.CallStatementSyntax node)
{
return SingleStatement(await node.Invocation.AcceptAsync<ExpressionSyntax>(_expressionVisitor));
return SingleStatement(await node.Invocation.AcceptAsync<CSharpSyntaxNode>(_expressionVisitor));
}

private static SyntaxList<StatementSyntax> SingleStatement(StatementSyntax statement)
{
return SyntaxFactory.SingletonList(statement);
}

private static SyntaxList<StatementSyntax> SingleStatement(ExpressionSyntax expression)
private static SyntaxList<StatementSyntax> SingleStatement(CSharpSyntaxNode node)
{
return SyntaxFactory.SingletonList<StatementSyntax>(SyntaxFactory.ExpressionStatement(expression));
return SingleStatement(node is StatementSyntax statement
? statement
: SyntaxFactory.ExpressionStatement((ExpressionSyntax)node));
}
}
38 changes: 23 additions & 15 deletions CodeConverter/CSharp/NameExpressionNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal class NameExpressionNodeVisitor
private readonly HashSet<string> _extraUsingDirectives;
private readonly Dictionary<string, Stack<(SyntaxNode Scope, string TempName)>> _tempNameForAnonymousScope;
private readonly Stack<ExpressionSyntax> _withBlockLhs;
private readonly Dictionary<VBSyntax.ConditionalAccessExpressionSyntax, ExpressionSyntax> _conditionalAccessReceivers;
private readonly ArgumentConverter _argumentConverter;

public CommonConversions CommonConversions { get; }
Expand All @@ -27,14 +28,16 @@ internal class NameExpressionNodeVisitor
public NameExpressionNodeVisitor(SemanticModel semanticModel, HashSet<string> generatedNames, ITypeContext typeContext, HashSet<string> extraUsingDirectives,
Dictionary<string, Stack<(SyntaxNode Scope, string TempName)>> tempNameForAnonymousScope, Stack<ExpressionSyntax> withBlockLhs, CommonConversions commonConversions,
ArgumentConverter argumentConverter,
CommentConvertingVisitorWrapper triviaConvertingExpressionVisitor)
CommentConvertingVisitorWrapper triviaConvertingExpressionVisitor,
Dictionary<VBSyntax.ConditionalAccessExpressionSyntax, ExpressionSyntax> conditionalAccessReceivers)
{
_semanticModel = semanticModel;
_generatedNames = generatedNames;
_typeContext = typeContext;
_extraUsingDirectives = extraUsingDirectives;
_tempNameForAnonymousScope = tempNameForAnonymousScope;
_withBlockLhs = withBlockLhs;
_conditionalAccessReceivers = conditionalAccessReceivers;
_argumentConverter = argumentConverter;
CommonConversions = commonConversions;
TriviaConvertingExpressionVisitor = triviaConvertingExpressionVisitor;
Expand Down Expand Up @@ -84,13 +87,15 @@ public async Task<CSharpSyntaxNode> ConvertMemberAccessExpressionAsync(VBasic.Sy
}
}
if (left == null) {
if (IsSubPartOfConditionalAccess(node)) {
return isDefaultProperty ? SyntaxFactory.ElementBindingExpression()
: await AdjustForImplicitInvocationAsync(node, SyntaxFactory.MemberBindingExpression(simpleNameSyntax));
if (GetConditionalAccess(node) is { } conditional) {
if (!_conditionalAccessReceivers.TryGetValue(conditional, out left)) {
return isDefaultProperty ? SyntaxFactory.ElementBindingExpression()
: await AdjustForImplicitInvocationAsync(node, SyntaxFactory.MemberBindingExpression(simpleNameSyntax));
}
} else if (node.IsParentKind(VBasic.SyntaxKind.NamedFieldInitializer) && _tempNameForAnonymousScope.TryGetValue(node.Name.Identifier.Text, out var stack) && stack.Any()) {
return ValidSyntaxFactory.IdentifierName(stack.Peek().TempName);
}
left = _withBlockLhs.Peek();
left ??= _withBlockLhs.Peek();
}

if (node.IsKind(VBasic.SyntaxKind.DictionaryAccessExpression)) {
Expand Down Expand Up @@ -207,7 +212,10 @@ public async Task<CSharpSyntaxNode> ConvertInvocationExpressionAsync(

if (node.Expression is null) {
var convertArgumentListOrEmptyAsync = await _argumentConverter.ConvertArgumentsAsync(node.ArgumentList);
return SyntaxFactory.ElementBindingExpression(SyntaxFactory.BracketedArgumentList(SyntaxFactory.SeparatedList(convertArgumentListOrEmptyAsync)));
var arguments = SyntaxFactory.BracketedArgumentList(SyntaxFactory.SeparatedList(convertArgumentListOrEmptyAsync));
return GetConditionalAccess(node) is { } conditional && _conditionalAccessReceivers.TryGetValue(conditional, out var receiver)
? SyntaxFactory.ElementAccessExpression(receiver, arguments)
: SyntaxFactory.ElementBindingExpression(arguments);
}

var convertedInvocation = await ConvertOrReplaceInvocationAsync(node, invocationSymbol);
Expand Down Expand Up @@ -708,21 +716,21 @@ private static QualifiedNameSyntax Qualify(string qualification, ExpressionSynta
(SimpleNameSyntax)toBeQualified);
}

private static bool IsSubPartOfConditionalAccess(VBasic.Syntax.MemberAccessExpressionSyntax node)
{
static bool IsMemberAccessChain(SyntaxNode exp) =>
exp?.IsKind(VBasic.SyntaxKind.InvocationExpression,
VBasic.SyntaxKind.SimpleMemberAccessExpression,
VBasic.SyntaxKind.ParenthesizedExpression,
VBasic.SyntaxKind.ConditionalAccessExpression) == true;
internal static bool IsMemberAccessChain(SyntaxNode exp) =>
exp is VBSyntax.XmlMemberAccessExpressionSyntax || exp?.IsKind(VBasic.SyntaxKind.InvocationExpression,
VBasic.SyntaxKind.SimpleMemberAccessExpression,
VBasic.SyntaxKind.ParenthesizedExpression,
VBasic.SyntaxKind.ConditionalAccessExpression) == true;

internal static VBSyntax.ConditionalAccessExpressionSyntax GetConditionalAccess(SyntaxNode node)
{
for (SyntaxNode child = node, parent = node.Parent; IsMemberAccessChain(parent); child = parent, parent = parent.Parent) {
if (parent is VBSyntax.ConditionalAccessExpressionSyntax cae && cae.WhenNotNull == child) {
return true; // On right hand side of a ?. conditional access
return cae;
}
}

return false;
return null;
}

private static CSharpSyntaxNode ReplaceRightmostIdentifierText(CSharpSyntaxNode expr, SyntaxToken idToken, string overrideIdentifier)
Expand Down
Loading
Loading