11using System ;
22using System . Collections ;
33using System . Collections . Generic ;
4+ using System . Collections . Immutable ;
45using System . Linq ;
6+ using System . Text ;
57using ICSharpCode . CodeConverter . Shared ;
68using ICSharpCode . CodeConverter . Util ;
79using 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