Skip to content

Commit cd7530e

Browse files
Merge pull request #183 from icsharpcode/quick-fixes
VB -> C#: Quick fixes
2 parents c1c5218 + c808d19 commit cd7530e

File tree

7 files changed

+169
-57
lines changed

7 files changed

+169
-57
lines changed

ICSharpCode.CodeConverter/CSharp/MethodBodyVisitor.cs

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4+
using System.Collections.Immutable;
45
using System.Linq;
6+
using System.Text;
57
using ICSharpCode.CodeConverter.Shared;
68
using ICSharpCode.CodeConverter.Util;
79
using Microsoft.CodeAnalysis;
@@ -16,19 +18,27 @@ namespace ICSharpCode.CodeConverter.CSharp
1618
{
1719
public partial class VisualBasicConverter
1820
{
21+
/// <summary>
22+
/// Maintains state relevant to the called method-like object. A fresh one must be used for each method, and the same one must be reused for statements in the same method
23+
/// </summary>
1924
class MethodBodyVisitor : VBasic.VisualBasicSyntaxVisitor<SyntaxList<StatementSyntax>>
2025
{
26+
private readonly VBasic.VisualBasicSyntaxNode _methodNode;
2127
private readonly SemanticModel _semanticModel;
2228
private readonly VBasic.VisualBasicSyntaxVisitor<CSharpSyntaxNode> _nodesVisitor;
2329
private readonly Stack<string> _withBlockTempVariableNames;
30+
private readonly HashSet<string> _generatedNames = new HashSet<string>();
2431

2532
public bool IsIterator { get; set; }
2633
public VBasic.VisualBasicSyntaxVisitor<SyntaxList<StatementSyntax>> CommentConvertingVisitor { get; }
2734

2835
private CommonConversions CommonConversions { get; }
2936

30-
public MethodBodyVisitor(SemanticModel semanticModel, VBasic.VisualBasicSyntaxVisitor<CSharpSyntaxNode> nodesVisitor, Stack<string> withBlockTempVariableNames, TriviaConverter triviaConverter)
37+
public MethodBodyVisitor(VBasic.VisualBasicSyntaxNode methodNode, SemanticModel semanticModel,
38+
VBasic.VisualBasicSyntaxVisitor<CSharpSyntaxNode> nodesVisitor,
39+
Stack<string> withBlockTempVariableNames, TriviaConverter triviaConverter)
3140
{
41+
_methodNode = methodNode;
3242
this._semanticModel = semanticModel;
3343
this._nodesVisitor = nodesVisitor;
3444
this._withBlockTempVariableNames = withBlockTempVariableNames;
@@ -150,22 +160,23 @@ public override SyntaxList<StatementSyntax> VisitRedimClause(VBSyntax.RedimClaus
150160
var oldArrayAssignment = CreateLocalVariableDeclarationAndAssignment(oldTargetName, csTargetArrayExpression);
151161

152162
var oldTargetExpression = SyntaxFactory.IdentifierName(oldTargetName);
153-
var arrayCopyIfNotNull = CreateConditionalArrayCopy(oldTargetExpression, csTargetArrayExpression, convertedBounds);
163+
var arrayCopyIfNotNull = CreateConditionalArrayCopy(node, oldTargetExpression, csTargetArrayExpression, convertedBounds);
154164

155165
return SyntaxFactory.List(new StatementSyntax[] {oldArrayAssignment, newArrayAssignment, arrayCopyIfNotNull});
156166
}
157167

158168
/// <summary>
159169
/// Cut down version of Microsoft.VisualBasic.CompilerServices.Utils.CopyArray
160170
/// </summary>
161-
private IfStatementSyntax CreateConditionalArrayCopy(IdentifierNameSyntax sourceArrayExpression,
171+
private IfStatementSyntax CreateConditionalArrayCopy(VBasic.VisualBasicSyntaxNode originalVbNode,
172+
IdentifierNameSyntax sourceArrayExpression,
162173
ExpressionSyntax targetArrayExpression,
163174
List<ExpressionSyntax> convertedBounds)
164175
{
165176
var sourceLength = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, sourceArrayExpression, SyntaxFactory.IdentifierName("Length"));
166177
var arrayCopyStatement = convertedBounds.Count == 1
167178
? CreateArrayCopyWithMinOfLengths(sourceArrayExpression, sourceLength, targetArrayExpression, convertedBounds.Single())
168-
: CreateArrayCopy(sourceArrayExpression, sourceLength, targetArrayExpression, convertedBounds);
179+
: CreateArrayCopy(originalVbNode, sourceArrayExpression, sourceLength, targetArrayExpression, convertedBounds);
169180

170181
var oldTargetNotEqualToNull = SyntaxFactory.BinaryExpression(SyntaxKind.NotEqualsExpression, sourceArrayExpression,
171182
SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression));
@@ -180,7 +191,8 @@ private IfStatementSyntax CreateConditionalArrayCopy(IdentifierNameSyntax source
180191
/// but existing VB code relying on the exception thrown from a multidimensional redim preserve on
181192
/// different rank arrays is hopefully rare enough that it's worth saving a few lines of code
182193
/// </remarks>
183-
private StatementSyntax CreateArrayCopy(IdentifierNameSyntax sourceArrayExpression,
194+
private StatementSyntax CreateArrayCopy(VBasic.VisualBasicSyntaxNode originalVbNode,
195+
IdentifierNameSyntax sourceArrayExpression,
184196
MemberAccessExpressionSyntax sourceLength,
185197
ExpressionSyntax targetArrayExpression, ICollection convertedBounds)
186198
{
@@ -192,7 +204,7 @@ private StatementSyntax CreateArrayCopy(IdentifierNameSyntax sourceArrayExpressi
192204
lastSourceLengthArgs);
193205
var length = SyntaxFactory.InvocationExpression(SyntaxFactory.ParseExpression("Math.Min"), ExpressionSyntaxExtensions.CreateArgList(sourceLastRankLength, targetLastRankLength));
194206

195-
var loopVariableName = GetUniqueVariableNameInScope(sourceArrayExpression, "i");
207+
var loopVariableName = GetUniqueVariableNameInScope(originalVbNode, "i");
196208
var loopVariableIdentifier = SyntaxFactory.IdentifierName(loopVariableName);
197209
var sourceStartForThisIteration =
198210
SyntaxFactory.BinaryExpression(SyntaxKind.MultiplyExpression, loopVariableIdentifier, sourceLastRankLength);
@@ -384,11 +396,7 @@ public override SyntaxList<StatementSyntax> VisitForBlock(VBSyntax.ForBlockSynta
384396
id = (ExpressionSyntax)stmt.ControlVariable.Accept(_nodesVisitor);
385397
var symbol = _semanticModel.GetSymbolInfo(stmt.ControlVariable).Symbol;
386398
if (!_semanticModel.LookupSymbols(node.FullSpan.Start, name: symbol.Name).Any()) {
387-
var variableDeclaratorSyntax = SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier(symbol.Name), null,
388-
SyntaxFactory.EqualsValueClause(startValue));
389-
declaration = SyntaxFactory.VariableDeclaration(
390-
SyntaxFactory.IdentifierName("var"),
391-
SyntaxFactory.SingletonSeparatedList(variableDeclaratorSyntax));
399+
declaration = CreateVariableDeclaration(symbol.Name, startValue);
392400
} else {
393401
startValue = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, id, startValue);
394402
}
@@ -397,22 +405,48 @@ public override SyntaxList<StatementSyntax> VisitForBlock(VBSyntax.ForBlockSynta
397405
var step = (ExpressionSyntax)stmt.StepClause?.StepValue.Accept(_nodesVisitor);
398406
PrefixUnaryExpressionSyntax value = step.SkipParens() as PrefixUnaryExpressionSyntax;
399407
ExpressionSyntax condition;
408+
409+
// In Visual Basic, the To expression is only evaluated once, but in C# will be evaluated every loop.
410+
// If it could evaluate differently or has side effects, it must be extracted as a variable
411+
var preLoopStatements = new List<SyntaxNode>();
412+
var csToValue = (ExpressionSyntax)stmt.ToValue.Accept(_nodesVisitor);
413+
if (!_semanticModel.GetConstantValue(stmt.ToValue).HasValue) {
414+
var loopToVariableName = GetUniqueVariableNameInScope(node, "loopTo");
415+
var loopEndDeclaration = SyntaxFactory.LocalDeclarationStatement(CreateVariableDeclaration(loopToVariableName, csToValue));
416+
// Does not do anything about porting newline trivia upwards to maintain spacing above the loop
417+
preLoopStatements.Add(loopEndDeclaration);
418+
csToValue = SyntaxFactory.IdentifierName(loopToVariableName);
419+
};
420+
400421
if (value == null) {
401-
condition = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, id, (ExpressionSyntax)stmt.ToValue.Accept(_nodesVisitor));
422+
condition = SyntaxFactory.BinaryExpression(SyntaxKind.LessThanOrEqualExpression, id, csToValue);
402423
} else {
403-
condition = SyntaxFactory.BinaryExpression(SyntaxKind.GreaterThanOrEqualExpression, id, (ExpressionSyntax)stmt.ToValue.Accept(_nodesVisitor));
424+
condition = SyntaxFactory.BinaryExpression(SyntaxKind.GreaterThanOrEqualExpression, id, csToValue);
404425
}
405426
if (step == null)
406427
step = SyntaxFactory.PostfixUnaryExpression(SyntaxKind.PostIncrementExpression, id);
407428
else
408429
step = SyntaxFactory.AssignmentExpression(SyntaxKind.AddAssignmentExpression, id, step);
409430
var block = SyntaxFactory.Block(node.Statements.SelectMany(s => s.Accept(CommentConvertingVisitor)));
410-
return SingleStatement(SyntaxFactory.ForStatement(
431+
var forStatementSyntax = SyntaxFactory.ForStatement(
411432
declaration,
412-
declaration != null ? SyntaxFactory.SeparatedList<ExpressionSyntax>() : SyntaxFactory.SingletonSeparatedList(startValue),
433+
declaration != null
434+
? SyntaxFactory.SeparatedList<ExpressionSyntax>()
435+
: SyntaxFactory.SingletonSeparatedList(startValue),
413436
condition,
414437
SyntaxFactory.SingletonSeparatedList(step),
415-
block.UnpackNonNestedBlock()));
438+
block.UnpackNonNestedBlock());
439+
return SyntaxFactory.List(preLoopStatements.Concat(new[] {forStatementSyntax}));
440+
}
441+
private static VariableDeclarationSyntax CreateVariableDeclaration(string variableName,
442+
ExpressionSyntax variableValue)
443+
{
444+
var variableDeclaratorSyntax = SyntaxFactory.VariableDeclarator(SyntaxFactory.Identifier(variableName), null,
445+
SyntaxFactory.EqualsValueClause(variableValue));
446+
var declaration = SyntaxFactory.VariableDeclaration(
447+
SyntaxFactory.IdentifierName("var"),
448+
SyntaxFactory.SingletonSeparatedList(variableDeclaratorSyntax));
449+
return declaration;
416450
}
417451

418452
public override SyntaxList<StatementSyntax> VisitForEachBlock(VBSyntax.ForEachBlockSyntax node)
@@ -537,11 +571,18 @@ private static VariableDeclarationSyntax CreateVariableDeclarationAndAssignment(
537571
return variableDeclarationSyntax;
538572
}
539573

540-
private string GetUniqueVariableNameInScope(SyntaxNode node, string variableNameBase)
574+
private string GetUniqueVariableNameInScope(VBasic.VisualBasicSyntaxNode node, string variableNameBase)
541575
{
542-
var reservedNames = _withBlockTempVariableNames.Concat(node.DescendantNodesAndSelf()
543-
.SelectMany(syntaxNode => _semanticModel.LookupSymbols(syntaxNode.SpanStart).Select(s => s.Name)));
544-
return NameGenerator.EnsureUniqueness(variableNameBase, reservedNames, true);
576+
// Need to check not just the symbols this node has access to, but whether there are any nested blocks which have access to this node and contain a conflicting name
577+
var scopeStarts = node.GetAncestorOrThis<VBSyntax.StatementSyntax>().DescendantNodesAndSelf()
578+
.OfType<VBSyntax.StatementSyntax>().Select(n => n.SpanStart).ToList();
579+
string uniqueName = NameGenerator.GenerateUniqueName(variableNameBase, string.Empty,
580+
n => {
581+
var matchingSymbols = scopeStarts.SelectMany(scopeStart => _semanticModel.LookupSymbols(scopeStart, name: n));
582+
return !_generatedNames.Contains(n) && !matchingSymbols.Any();
583+
});
584+
_generatedNames.Add(uniqueName);
585+
return uniqueName;
545586
}
546587

547588
public override SyntaxList<StatementSyntax> VisitTryBlock(VBSyntax.TryBlockSyntax node)

0 commit comments

Comments
 (0)