Skip to content

Commit

Permalink
VB -> CS: Continue the correct of several continuable nested blocks - f…
Browse files Browse the repository at this point in the history
…ixes #946
  • Loading branch information
GrahamTheCoder committed Oct 1, 2022
1 parent 7f10fbb commit c29ef41
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 7 deletions.
6 changes: 3 additions & 3 deletions CodeConverter/CSharp/IHoistedNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ namespace ICSharpCode.CodeConverter.CSharp;
internal interface IHoistedNode
{
}
internal record IfTrueBreak(ExpressionSyntax Condition) : IHoistedNode

internal record PostIfTrueBlock(ExpressionSyntax Condition, StatementSyntax Statement) : IHoistedNode
{
public StatementSyntax CreateIfTrueBreakStatement() => SyntaxFactory.IfStatement(Condition, SyntaxFactory.Block(SyntaxFactory.BreakStatement()));
public StatementSyntax CreateIfTrueBreakStatement() => SyntaxFactory.IfStatement(Condition, SyntaxFactory.Block(Statement));
}
3 changes: 2 additions & 1 deletion CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,8 @@ public override async Task<SyntaxList<StatementSyntax>> VisitReturnStatement(VBS

public override async Task<SyntaxList<StatementSyntax>> VisitContinueStatement(VBSyntax.ContinueStatementSyntax node)
{
return SingleStatement(SyntaxFactory.ContinueStatement());
var vbBlockKeywordKind = VBasic.VisualBasicExtensions.Kind(node.BlockKeyword);
return SyntaxFactory.List(_perScopeState.ConvertContinue(vbBlockKeywordKind));
}

public override async Task<SyntaxList<StatementSyntax>> VisitYieldStatement(VBSyntax.YieldStatementSyntax node)
Expand Down
28 changes: 26 additions & 2 deletions CodeConverter/CSharp/PerScopeState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ private StatementSyntax[] GetPostStatements()
{
var scopeState = _hoistedNodesPerScope.Peek();
return scopeState.OfType<AdditionalAssignment>().Select(AdditionalAssignment.CreateAssignment)
.Concat(scopeState.OfType<IfTrueBreak>().Select(arg => arg.CreateIfTrueBreakStatement()))
.Concat(scopeState.OfType<PostIfTrueBlock>().Select(arg => arg.CreateIfTrueBreakStatement()))
.ToArray();
}

Expand Down Expand Up @@ -195,12 +195,36 @@ public IEnumerable<StatementSyntax> ConvertExit(VBasic.SyntaxKind vbBlockKeyword
var assignmentExpression = CommonConversions.Literal(true);
foreach (var scope in scopesToExit) {
var exitScopeVar = new AdditionalDeclaration("exit" + VBasic.SyntaxFactory.Token(vbBlockKeywordKind), CommonConversions.Literal(false), SyntaxFactory.ParseTypeName("bool"));
var ifTrueBreak = new IfTrueBreak(exitScopeVar.IdentifierName);
var ifTrueBreak = new PostIfTrueBlock(exitScopeVar.IdentifierName, SyntaxFactory.BreakStatement());
scope.HoistedNodes.Add(exitScopeVar);
scope.HoistedNodes.Add(ifTrueBreak);
assignmentExpression = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, exitScopeVar.IdentifierName, assignmentExpression);
}
if (scopesToExit.Any()) yield return SyntaxFactory.ExpressionStatement(assignmentExpression);
yield return SyntaxFactory.BreakStatement();
}

public IEnumerable<StatementSyntax> ConvertContinue(VBasic.SyntaxKind vbBlockKeywordKind)
{
var scopesToExit = _hoistedNodesPerScope.Where(x => x.ExitableKind is not VBasic.SyntaxKind.None).TakeWhile(x => x.ExitableKind != vbBlockKeywordKind && x.IsBreakableInCs).ToArray();
// Select is breakable, but not continuable, so only need to break out of it on the way to something else, not if it's last.
scopesToExit = scopesToExit.Reverse().SkipWhile(x => x.ExitableKind is VBasic.SyntaxKind.SelectKeyword).Reverse().ToArray();
var assignmentExpression = CommonConversions.Literal(true);
int i = 0;
foreach (var scope in scopesToExit) {
var continueScopeVar = new AdditionalDeclaration("continue" + VBasic.SyntaxFactory.Token(vbBlockKeywordKind), CommonConversions.Literal(false), SyntaxFactory.ParseTypeName("bool"));
StatementSyntax stmt = i++ == scopesToExit.Length - 1 ? SyntaxFactory.ContinueStatement() : SyntaxFactory.BreakStatement();
var ifTrue = new PostIfTrueBlock(continueScopeVar.IdentifierName, stmt);
scope.HoistedNodes.Add(continueScopeVar);
scope.HoistedNodes.Add(ifTrue);
assignmentExpression = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, continueScopeVar.IdentifierName, assignmentExpression);
}

if (scopesToExit.Any()) {
yield return SyntaxFactory.ExpressionStatement(assignmentExpression);
yield return SyntaxFactory.BreakStatement();
} else {
yield return SyntaxFactory.ContinueStatement();
}
}
}
240 changes: 240 additions & 0 deletions Tests/CSharp/StatementTests/ExitableMethodExecutableStatementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,246 @@ public void Test()
}");
}

[Fact]
public async Task MultipleBreakable_CreatesIfStatementsToExitContainingBlock_WithoutRunningInterveningCodeAsync()
{
await TestConversionVisualBasicToCSharpAsync(@"Imports System.Collections.Generic
Public Class VisualBasicClass
Public Sub Test
Dim LstTmp As New List(Of Integer)
LstTmp.Add(5)
LstTmp.Add(6)
LstTmp.Add(7)
Dim i_Total As Integer
For Each CurVal As Integer In LstTmp
i_Total += CurVal
Select Case CurVal
Case 6
Exit For
End Select
Console.WriteLine()
Next
system.Console.WriteLine(i_Total.ToString())
End Sub
End Class", @"using System;
using System.Collections.Generic;
public partial class VisualBasicClass
{
public void Test()
{
var LstTmp = new List<int>();
LstTmp.Add(5);
LstTmp.Add(6);
LstTmp.Add(7);
var i_Total = default(int);
foreach (int CurVal in LstTmp)
{
i_Total += CurVal;
bool exitFor = false;
switch (CurVal)
{
case 6:
{
exitFor = true;
break;
}
}
if (exitFor)
{
break;
}
Console.WriteLine();
}
Console.WriteLine(i_Total.ToString());
}
}");
}

[Fact]
public async Task MultipleBreakable_CreatesIfStatementsToExitContainingBlockIssue946Async()
{
await TestConversionVisualBasicToCSharpAsync(@"Imports System
Imports System.Collections.Generic
Public Class VisualBasicClass
Public Function Test(applicationRoles)
For Each appRole In applicationRoles
Dim objectUnit = appRole
While objectUnit IsNot Nothing
If appRole < 10 Then
If appRole < 5 Then
Return True
Else
Continue For
End If
End IF
objectUnit = objectUnit.ToString
End While
Next
End Function
End Class", @"using System.Collections;
using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
public partial class VisualBasicClass
{
public object Test(object applicationRoles)
{
foreach (var appRole in (IEnumerable)applicationRoles)
{
var objectUnit = appRole;
bool continueFor = false;
while (objectUnit is not null)
{
if (Conversions.ToBoolean(Operators.ConditionalCompareObjectLess(appRole, 10, false)))
{
if (Conversions.ToBoolean(Operators.ConditionalCompareObjectLess(appRole, 5, false)))
{
return true;
}
else
{
continueFor = true;
break;
}
}
objectUnit = objectUnit.ToString();
}
if (continueFor)
{
continue;
}
}
return default;
}
}");
}

[Fact]
public async Task BreakableThenContinuable_CreatesIfStatementsToExitContainingBlock_WithoutRunningInterveningCodeAsync()
{
await TestConversionVisualBasicToCSharpAsync(@"Imports System.Collections.Generic
Public Class VisualBasicClass
Public Sub Test
Dim LstTmp As New List(Of Integer)
LstTmp.Add(5)
LstTmp.Add(6)
LstTmp.Add(7)
Dim i_Total As Integer
For Each CurVal As Integer In LstTmp
i_Total += CurVal
Select Case CurVal
Case 6
Continue For
End Select
Console.WriteLine()
Next
system.Console.WriteLine(i_Total.ToString())
End Sub
End Class", @"using System;
using System.Collections.Generic;
public partial class VisualBasicClass
{
public void Test()
{
var LstTmp = new List<int>();
LstTmp.Add(5);
LstTmp.Add(6);
LstTmp.Add(7);
var i_Total = default(int);
foreach (int CurVal in LstTmp)
{
i_Total += CurVal;
switch (CurVal)
{
case 6:
{
continue;
}
}
Console.WriteLine();
}
Console.WriteLine(i_Total.ToString());
}
}");
}

[Fact]
public async Task MultipleContinuable_CreatesIfStatementsToExitContainingBlock_WithoutRunningInterveningCodeAsync()
{
await TestConversionVisualBasicToCSharpAsync(@"Imports System
Imports System.Collections.Generic
Public Class VisualBasicClass
Public Sub Test
Dim LstTmp As New List(Of Integer)
LstTmp.Add(5)
LstTmp.Add(6)
LstTmp.Add(7)
Dim i_Total As Integer
For Each CurVal As Integer In LstTmp
i_Total += CurVal
While CurVal < 3
Select Case CurVal
Case 6
Continue For
End Select
End While
Console.WriteLine()
Next
System.Console.WriteLine(i_Total.ToString())
End Sub
End Class", @"using System;
using System.Collections.Generic;
public partial class VisualBasicClass
{
public void Test()
{
var LstTmp = new List<int>();
LstTmp.Add(5);
LstTmp.Add(6);
LstTmp.Add(7);
var i_Total = default(int);
foreach (int CurVal in LstTmp)
{
i_Total += CurVal;
bool continueFor1 = false;
while (CurVal < 3)
{
bool continueFor = false;
switch (CurVal)
{
case 6:
{
continueFor1 = continueFor = true;
break;
}
}
if (continueFor)
{
break;
}
}
if (continueFor1)
{
continue;
}
Console.WriteLine();
}
Console.WriteLine(i_Total.ToString());
}
}");
}

[Fact]
public async Task ExitTry_CreatesBreakableLoop_Issue779Async()
{
Expand Down
2 changes: 1 addition & 1 deletion Tests/TestConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static class TestConstants
/// Set to false
/// Commit
/// </summary>
public static bool RecharacterizeByWritingExpectedOverActual => false;
public static bool RecharacterizeByWritingExpectedOverActual => true;

public static string GetTestDataDirectory()
{
Expand Down

0 comments on commit c29ef41

Please sign in to comment.