|
2 | 2 | using System.Data; |
3 | 3 | using System.Globalization; |
4 | 4 | using System.Linq.Expressions; |
| 5 | +using System.Runtime.CompilerServices; |
5 | 6 | using System.Xml.Linq; |
6 | 7 | using ICSharpCode.CodeConverter.CSharp.Replacements; |
7 | 8 | using ICSharpCode.CodeConverter.Util.FromRoslyn; |
@@ -845,30 +846,35 @@ private CSharpSyntaxNode ConvertAddressOf(VBSyntax.UnaryExpressionSyntax node, E |
845 | 846 | return expr; |
846 | 847 | } |
847 | 848 |
|
848 | | - public override async Task<CSharpSyntaxNode> VisitBinaryExpression(VBasic.Syntax.BinaryExpressionSyntax node) |
| 849 | + public override async Task<CSharpSyntaxNode> VisitBinaryExpression(VBasic.Syntax.BinaryExpressionSyntax entryNode) |
849 | 850 | { |
850 | | - // Manually walk tree bottom up to avoid stack overflow for deeply nested binary expressions like 3 + 4 + 5 + ... |
| 851 | + // Walk down the syntax tree for deeply nested binary expressions to avoid stack overflow |
| 852 | + // e.g. 3 + 4 + 5 + ... |
851 | 853 | // Test "DeeplyNestedBinaryExpressionShouldNotStackOverflowAsync()" skipped because it's too slow |
852 | | - while (node.Left is VBSyntax.BinaryExpressionSyntax l) { |
853 | | - node = l; |
854 | | - } |
855 | 854 |
|
856 | 855 | ExpressionSyntax csLhs = null; |
857 | | - for (; node is not null; node = BinaryParentWithLhs(node)) { |
858 | | - csLhs = (ExpressionSyntax) await ConvertBinaryExpressionAsync(node, csLhs); |
| 856 | + int levelsToConvert = 0; |
| 857 | + VBSyntax.BinaryExpressionSyntax currentNode = entryNode; |
| 858 | + |
| 859 | + // Walk down the nested levels to count them |
| 860 | + for (var nextNode = entryNode; nextNode != null; currentNode = nextNode, nextNode = currentNode.Left as VBSyntax.BinaryExpressionSyntax, levelsToConvert++) { |
| 861 | + // Don't go beyond a rewritten operator because that code has many paths that can call VisitBinaryExpression. Passing csLhs through all of that would harm the code quality more than it's worth to help that edge case. |
| 862 | + if (await RewriteBinaryOperatorOrNullAsync(nextNode) is { } operatorNode) { |
| 863 | + csLhs = operatorNode; |
| 864 | + break; |
| 865 | + } |
859 | 866 | } |
860 | 867 |
|
861 | | - return csLhs; |
| 868 | + // Walk back up the same levels converting as we go. |
| 869 | + for (; levelsToConvert > 0; currentNode = currentNode!.Parent as VBSyntax.BinaryExpressionSyntax, levelsToConvert--) { |
| 870 | + csLhs = (ExpressionSyntax)await ConvertBinaryExpressionAsync(currentNode, csLhs); |
| 871 | + } |
862 | 872 |
|
863 | | - VBSyntax.BinaryExpressionSyntax BinaryParentWithLhs(SyntaxNode currentSyntaxNode) => currentSyntaxNode.Parent is VBSyntax.BinaryExpressionSyntax {Left: var l} p && l == currentSyntaxNode ? p : null; |
| 873 | + return csLhs; |
864 | 874 | } |
865 | 875 |
|
866 | 876 | private async Task<CSharpSyntaxNode> ConvertBinaryExpressionAsync(VBasic.Syntax.BinaryExpressionSyntax node, ExpressionSyntax lhs = null, ExpressionSyntax rhs = null) |
867 | 877 | { |
868 | | - if (await _operatorConverter.ConvertRewrittenBinaryOperatorOrNullAsync(node, TriviaConvertingExpressionVisitor.IsWithinQuery) is { } operatorNode) { |
869 | | - return operatorNode; |
870 | | - } |
871 | | - |
872 | 878 | lhs ??= await node.Left.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor); |
873 | 879 | rhs ??= await node.Right.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor); |
874 | 880 |
|
@@ -926,6 +932,9 @@ private async Task<CSharpSyntaxNode> ConvertBinaryExpressionAsync(VBasic.Syntax. |
926 | 932 | return node.Parent.IsKind(VBasic.SyntaxKind.SimpleArgument) ? exp : exp.AddParens(); |
927 | 933 | } |
928 | 934 |
|
| 935 | + private async Task<ExpressionSyntax> RewriteBinaryOperatorOrNullAsync(VBSyntax.BinaryExpressionSyntax node) => |
| 936 | + await _operatorConverter.ConvertRewrittenBinaryOperatorOrNullAsync(node, TriviaConvertingExpressionVisitor.IsWithinQuery); |
| 937 | + |
929 | 938 | private async Task<CSharpSyntaxNode> WithRemovedRedundantConversionOrNullAsync(VBSyntax.InvocationExpressionSyntax conversionNode, ISymbol invocationSymbol) |
930 | 939 | { |
931 | 940 | if (invocationSymbol?.ContainingNamespace.MetadataName != nameof(Microsoft.VisualBasic) || |
|
0 commit comments