Skip to content

Commit c4a884e

Browse files
Handle boolean for loop "To" expression - fixes #602
1 parent c8a82bd commit c4a884e

3 files changed

Lines changed: 32 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
2727
* Coercing enum to a string now correctly uses its numeric value [#590](https://github.com/icsharpcode/CodeConverter/issues/590)
2828
* Correct conversion for equality of overloaded types [#594](https://github.com/icsharpcode/CodeConverter/issues/594)
2929
* Correct conversion when for loop variable is a class member [#601](https://github.com/icsharpcode/CodeConverter/issues/601)
30+
* Correct conversion when for loop "To" expression is a boolean [#602](https://github.com/icsharpcode/CodeConverter/issues/602)
3031

3132
### C# -> VB
3233

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -516,27 +516,31 @@ public override async Task<SyntaxList<StatementSyntax>> VisitForBlock(VBSyntax.F
516516
}
517517
}
518518

519-
// In Visual Basic, the To expression is only evaluated once, but in C# will be evaluated every loop.
520-
// If it could evaluate differently or has side effects, it must be extracted as a variable
519+
521520
var preLoopStatements = new List<SyntaxNode>();
522521
var csToValue = await stmt.ToValue.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
522+
csToValue = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(stmt.ToValue, csToValue?.SkipIntoParens(), forceTargetType: controlVarType);
523+
524+
// In Visual Basic, the To expression is only evaluated once, but in C# will be evaluated every loop.
525+
// If it could evaluate differently or has side effects, it must be extracted as a variable
523526
if (!_semanticModel.GetConstantValue(stmt.ToValue).HasValue) {
524527
var loopToVariableName = GetUniqueVariableNameInScope(node, "loopTo");
525528
var toValueType = _semanticModel.GetTypeInfo(stmt.ToValue).ConvertedType;
526529
var toVariableId = SyntaxFactory.IdentifierName(loopToVariableName);
530+
531+
// If that variable has the same type as the loop variable, we can explicitly declare the type and it inline
527532
if (controlVarType?.Equals(toValueType) == true && declaration != null) {
528533
var loopToAssignment = CommonConversions.CreateVariableDeclarator(loopToVariableName, csToValue);
529-
declaration = declaration.AddVariables(loopToAssignment);
534+
declaration = declaration.AddVariables(loopToAssignment).WithType(CommonConversions.GetTypeSyntax(controlVarType));
530535
} else {
531-
csToValue = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(stmt.ToValue, csToValue, forceTargetType: controlVarType);
532536
var loopEndDeclaration = SyntaxFactory.LocalDeclarationStatement(
533537
CommonConversions.CreateVariableDeclarationAndAssignment(loopToVariableName, csToValue));
534538
// Does not do anything about porting newline trivia upwards to maintain spacing above the loop
535539
preLoopStatements.Add(loopEndDeclaration);
536540
}
537541

538542
csToValue = toVariableId;
539-
};
543+
}
540544

541545
var (csCondition, csStep) = await ConvertConditionAndStepClauseAsync(stmt, id, csToValue, controlVarType);
542546

@@ -562,7 +566,6 @@ public override async Task<SyntaxList<StatementSyntax>> VisitForBlock(VBSyntax.F
562566
// For an enum, you need to add on an integer for example:
563567
var forceStepType = controlVarType is INamedTypeSymbol nt && nt.IsEnumType() ? nt.EnumUnderlyingType : controlVarType;
564568
csStepValue = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(vbStepValue, csStepValue?.SkipIntoParens(), forceTargetType: forceStepType);
565-
csToValue = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(stmt.ToValue, csToValue?.SkipIntoParens(), forceTargetType: controlVarType);
566569

567570
var nonNegativeCondition = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, id, csToValue);
568571
var negativeCondition = SyntaxFactory.BinaryExpression(SyntaxKind.GreaterThanOrEqualExpression, id, csToValue);

Tests/CSharp/SpecialConversionTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,28 @@ public object GetItem(DataRow dr)
243243
const string 字 = ""字"";
244244
return default;
245245
}
246+
}");
247+
}
248+
249+
[Fact]
250+
public async Task UsingBoolInToExpressionAsync()
251+
{
252+
// Beware, this will never enter the loop, it's buggy input due to the "i <", but it compiles and runs, so the output should too (and do the same thing)
253+
await TestConversionVisualBasicToCSharpAsync(@"Public Class C
254+
Public Sub M(OldWords As String(), NewWords As String(), HTMLCode As String)
255+
For i As Integer = 0 To i < OldWords.Length - 1
256+
HTMLCode = HTMLCode.Replace(OldWords(i), NewWords(i))
257+
Next i
258+
End Sub
259+
End Class", @"using Microsoft.VisualBasic.CompilerServices; // Install-Package Microsoft.VisualBasic
260+
261+
public partial class C
262+
{
263+
public void M(string[] OldWords, string[] NewWords, string HTMLCode)
264+
{
265+
for (int i = 0, loopTo = Conversions.ToInteger(i < OldWords.Length - 1); i <= loopTo; i++)
266+
HTMLCode = HTMLCode.Replace(OldWords[i], NewWords[i]);
267+
}
246268
}");
247269
}
248270
}

0 commit comments

Comments
 (0)