Skip to content

Commit 325115e

Browse files
Converting ForEach loop: avoid duplicate variable compilation issue
Fixes #558
1 parent c4e5f88 commit 325115e

File tree

3 files changed

+45
-11
lines changed

3 files changed

+45
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1212

1313
* Improve multi-declaration field conversion for arrrays - [#559](https://github.com/icsharpcode/CodeConverter/issues/559)
1414
* Add parentheses around ternary statement - [#565](https://github.com/icsharpcode/CodeConverter/issues/565)
15+
* When converting ForEach loop, avoid duplicate variable compilation issue [#558](https://github.com/icsharpcode/CodeConverter/issues/558)
1516

1617
### C# -> VB
1718

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,17 +539,23 @@ public override async Task<SyntaxList<StatementSyntax>> VisitForEachBlock(VBSynt
539539

540540
TypeSyntax type;
541541
SyntaxToken id;
542+
List<StatementSyntax> statements = new List<StatementSyntax>();
542543
if (stmt.ControlVariable is VBSyntax.VariableDeclaratorSyntax vds) {
543544
var declaration = (await CommonConversions.SplitVariableDeclarations(vds)).Variables.Single().Decl;
544545
type = declaration.Type;
545546
id = declaration.Variables.Single().Identifier;
547+
} else if (_semanticModel.GetSymbolInfo(stmt.ControlVariable).Symbol is ISymbol varSymbol) {
548+
var v = (IdentifierNameSyntax) await stmt.ControlVariable.AcceptAsync(_expressionVisitor);
549+
id = CommonConversions.CsEscapedIdentifier(GetUniqueVariableNameInScope(node, "current" + varSymbol.Name.ToPascalCase()));
550+
statements.Add(SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, v, SyntaxFactory.IdentifierName(id))));
551+
type = CommonConversions.GetTypeSyntax(varSymbol.GetSymbolType());
546552
} else {
547553
var v = (IdentifierNameSyntax) await stmt.ControlVariable.AcceptAsync(_expressionVisitor);
548554
id = v.Identifier;
549555
type = SyntaxFactory.ParseTypeName("var");
550556
}
551557

552-
var block = SyntaxFactory.Block(await ConvertStatements(node.Statements));
558+
var block = SyntaxFactory.Block(statements.Concat(await ConvertStatements(node.Statements)));
553559
var csExpression = (ExpressionSyntax)await stmt.Expression.AcceptAsync(_expressionVisitor);
554560
return SingleStatement(SyntaxFactory.ForEachStatement(
555561
type,

Tests/CSharp/StatementTests/LoopStatementTests.cs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,21 +138,21 @@ public async Task ForEachStatementWithExplicitTypeAsync()
138138
{
139139
await TestConversionVisualBasicToCSharpAsync(@"Class TestClass
140140
Private Sub TestMethod(ByVal values As Integer())
141-
For Each val As Integer In values
142-
If val = 2 Then Continue For
143-
If val = 3 Then Exit For
141+
For Each v As Integer In values
142+
If v = 2 Then Continue For
143+
If v = 3 Then Exit For
144144
Next
145145
End Sub
146146
End Class", @"
147147
internal partial class TestClass
148148
{
149149
private void TestMethod(int[] values)
150150
{
151-
foreach (int val in values)
151+
foreach (int v in values)
152152
{
153-
if (val == 2)
153+
if (v == 2)
154154
continue;
155-
if (val == 3)
155+
if (v == 3)
156156
break;
157157
}
158158
}
@@ -164,6 +164,33 @@ public async Task ForEachStatementWithVarAsync()
164164
{
165165
await TestConversionVisualBasicToCSharpAsync(@"Class TestClass
166166
Private Sub TestMethod(ByVal values As Integer())
167+
For Each v In values
168+
If v = 2 Then Continue For
169+
If v = 3 Then Exit For
170+
Next
171+
End Sub
172+
End Class", @"
173+
internal partial class TestClass
174+
{
175+
private void TestMethod(int[] values)
176+
{
177+
foreach (var v in values)
178+
{
179+
if (v == 2)
180+
continue;
181+
if (v == 3)
182+
break;
183+
}
184+
}
185+
}");
186+
}
187+
188+
[Fact]
189+
public async Task ForEachStatementWithOuterDeclarationAsync()
190+
{
191+
await TestConversionVisualBasicToCSharpAsync(@"Class TestClass
192+
Private Sub TestMethod(ByVal values As Integer())
193+
Dim val As Integer
167194
For Each val In values
168195
If val = 2 Then Continue For
169196
If val = 3 Then Exit For
@@ -174,17 +201,17 @@ internal partial class TestClass
174201
{
175202
private void TestMethod(int[] values)
176203
{
177-
foreach (var val in values)
204+
int val;
205+
foreach (int currentVal in values)
178206
{
207+
val = currentVal;
179208
if (val == 2)
180209
continue;
181210
if (val == 3)
182211
break;
183212
}
184213
}
185-
}
186-
1 source compilation errors:
187-
BC30516: Overload resolution failed because no accessible 'Val' accepts this number of arguments.");
214+
}");
188215
}
189216

190217
[Fact]

0 commit comments

Comments
 (0)