Skip to content

Commit 0532ed5

Browse files
Fix VB With block conversion with null-conditional operator
Modified IsSubPartOfConditionalAccess to recursively check if a member access is part of the WhenNotNull branch of a ConditionalAccessExpression, handling nested conditional accesses. This ensures that the expression part (LHS) of a conditional access is correctly identified as not being part of the binding chain, allowing it to be resolved to the With block variable. Added regression test WithBlockWithNullConditionalAccessAsync. Co-authored-by: GrahamTheCoder <2490482+GrahamTheCoder@users.noreply.github.com>
1 parent 355bc35 commit 0532ed5

2 files changed

Lines changed: 74 additions & 9 deletions

File tree

CodeConverter/CSharp/NameExpressionNodeVisitor.cs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Data;
1+
using System.Data;
22
using System.Globalization;
33
using ICSharpCode.CodeConverter.CSharp.Replacements;
44
using ICSharpCode.CodeConverter.Util.FromRoslyn;
@@ -688,14 +688,39 @@ private static QualifiedNameSyntax Qualify(string qualification, ExpressionSynta
688688

689689
private static bool IsSubPartOfConditionalAccess(VBasic.Syntax.MemberAccessExpressionSyntax node)
690690
{
691-
var firstPossiblyConditionalAncestor = node.Parent;
692-
while (firstPossiblyConditionalAncestor != null &&
693-
firstPossiblyConditionalAncestor.IsKind(VBasic.SyntaxKind.InvocationExpression,
694-
VBasic.SyntaxKind.SimpleMemberAccessExpression)) {
695-
firstPossiblyConditionalAncestor = firstPossiblyConditionalAncestor.Parent;
691+
SyntaxNode child = node;
692+
SyntaxNode parent = node.Parent;
693+
694+
while (parent != null)
695+
{
696+
if (parent.IsKind(VBasic.SyntaxKind.InvocationExpression,
697+
VBasic.SyntaxKind.SimpleMemberAccessExpression,
698+
VBasic.SyntaxKind.ParenthesizedExpression))
699+
{
700+
child = parent;
701+
parent = parent.Parent;
702+
continue;
703+
}
704+
705+
if (parent is VBSyntax.ConditionalAccessExpressionSyntax cae)
706+
{
707+
if (cae.WhenNotNull == child)
708+
{
709+
return true;
710+
}
711+
712+
if (cae.Expression == child)
713+
{
714+
child = parent;
715+
parent = parent.Parent;
716+
continue;
717+
}
718+
}
719+
720+
break;
696721
}
697722

698-
return firstPossiblyConditionalAncestor?.IsKind(VBasic.SyntaxKind.ConditionalAccessExpression) == true;
723+
return false;
699724
}
700725

701726
private static CSharpSyntaxNode ReplaceRightmostIdentifierText(CSharpSyntaxNode expr, SyntaxToken idToken, string overrideIdentifier)

Tests/CSharp/StatementTests/MethodStatementTests.cs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Threading.Tasks;
1+
using System.Threading.Tasks;
22
using ICSharpCode.CodeConverter.Tests.TestRunners;
33
using Xunit;
44

@@ -1673,4 +1673,44 @@ public object Func()
16731673
}
16741674
}");
16751675
}
1676-
}
1676+
1677+
[Fact]
1678+
public async Task WithBlockWithNullConditionalAccessAsync()
1679+
{
1680+
await TestConversionVisualBasicToCSharpAsync(@"
1681+
Public Class Class1
1682+
Public Property x As Class1
1683+
Public Property Name As String
1684+
End Class
1685+
1686+
Public Class TestClass
1687+
Private _Data As Class1
1688+
Private x As String
1689+
1690+
Public Sub TestMethod()
1691+
With _Data
1692+
x = .x?.Name
1693+
End With
1694+
End Sub
1695+
End Class", @"
1696+
public partial class Class1
1697+
{
1698+
public Class1 x { get; set; }
1699+
public string Name { get; set; }
1700+
}
1701+
1702+
public partial class TestClass
1703+
{
1704+
private Class1 _Data;
1705+
private string x;
1706+
1707+
public void TestMethod()
1708+
{
1709+
{
1710+
ref var withBlock = ref _Data;
1711+
x = withBlock.x?.Name;
1712+
}
1713+
}
1714+
}");
1715+
}
1716+
}

0 commit comments

Comments
 (0)