|
| 1 | +using System.Collections.Immutable; |
| 2 | +//using System.Data; // Not directly used by lambda methods |
| 3 | +using System.Globalization; |
| 4 | +//using System.Linq.Expressions; // Not directly used by lambda methods |
| 5 | +//using System.Runtime.CompilerServices; |
| 6 | +//using System.Xml.Linq; // Not used |
| 7 | +//using ICSharpCode.CodeConverter.CSharp.Replacements; // Not directly used |
| 8 | +using ICSharpCode.CodeConverter.Util.FromRoslyn; // For .Yield() |
| 9 | +using Microsoft.CodeAnalysis.CSharp; |
| 10 | +using Microsoft.CodeAnalysis.CSharp.Syntax; |
| 11 | +//using Microsoft.CodeAnalysis.Operations; // Not directly used |
| 12 | +//using Microsoft.CodeAnalysis.Simplification; // Not directly used |
| 13 | +//using Microsoft.VisualBasic; // Not directly used |
| 14 | +//using Microsoft.VisualBasic.CompilerServices; // Not directly used |
| 15 | +//using ComparisonKind = ICSharpCode.CodeConverter.CSharp.VisualBasicEqualityComparison.ComparisonKind; // Not used |
| 16 | +using System.Threading.Tasks; |
| 17 | +using System; |
| 18 | +using System.Linq; |
| 19 | +using System.Collections.Generic; |
| 20 | +using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; |
| 21 | + |
| 22 | +namespace ICSharpCode.CodeConverter.CSharp; |
| 23 | + |
| 24 | +internal partial class ExpressionNodeVisitor // Must be partial |
| 25 | +{ |
| 26 | + private readonly LambdaConverter _lambdaConverter; |
| 27 | + |
| 28 | + public override async Task<CSharpSyntaxNode> VisitSingleLineLambdaExpression(VBasic.Syntax.SingleLineLambdaExpressionSyntax node) |
| 29 | + { |
| 30 | + var originalIsWithinQuery = TriviaConvertingExpressionVisitor.IsWithinQuery; |
| 31 | + TriviaConvertingExpressionVisitor.IsWithinQuery = CommonConversions.IsLinqDelegateExpression(node); |
| 32 | + try { |
| 33 | + return await ConvertInnerAsync(); |
| 34 | + } finally { |
| 35 | + TriviaConvertingExpressionVisitor.IsWithinQuery = originalIsWithinQuery; |
| 36 | + } |
| 37 | + |
| 38 | + async Task<CSharpSyntaxNode> ConvertInnerAsync() |
| 39 | + { |
| 40 | + IReadOnlyCollection<StatementSyntax> convertedStatements; |
| 41 | + if (node.Body is VBasic.Syntax.StatementSyntax statement) |
| 42 | + { |
| 43 | + convertedStatements = await ConvertMethodBodyStatementsAsync(statement, statement.Yield().ToArray()); |
| 44 | + } |
| 45 | + else |
| 46 | + { |
| 47 | + var csNode = await node.Body.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor); |
| 48 | + convertedStatements = new[] {SyntaxFactory.ExpressionStatement(csNode)}; |
| 49 | + } |
| 50 | + |
| 51 | + var param = await node.SubOrFunctionHeader.ParameterList.AcceptAsync<ParameterListSyntax>(TriviaConvertingExpressionVisitor); |
| 52 | + return await _lambdaConverter.ConvertAsync(node, param, convertedStatements); |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + public override async Task<CSharpSyntaxNode> VisitMultiLineLambdaExpression(VBasic.Syntax.MultiLineLambdaExpressionSyntax node) |
| 57 | + { |
| 58 | + var originalIsWithinQuery = TriviaConvertingExpressionVisitor.IsWithinQuery; |
| 59 | + TriviaConvertingExpressionVisitor.IsWithinQuery = CommonConversions.IsLinqDelegateExpression(node); |
| 60 | + try { |
| 61 | + return await ConvertInnerAsync(); |
| 62 | + } finally { |
| 63 | + TriviaConvertingExpressionVisitor.IsWithinQuery = originalIsWithinQuery; |
| 64 | + } |
| 65 | + |
| 66 | + async Task<CSharpSyntaxNode> ConvertInnerAsync() |
| 67 | + { |
| 68 | + var body = await ConvertMethodBodyStatementsAsync(node, node.Statements); |
| 69 | + var param = await node.SubOrFunctionHeader.ParameterList.AcceptAsync<ParameterListSyntax>(TriviaConvertingExpressionVisitor); |
| 70 | + return await _lambdaConverter.ConvertAsync(node, param, body.ToList()); |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + public async Task<IReadOnlyCollection<StatementSyntax>> ConvertMethodBodyStatementsAsync(VBasic.VisualBasicSyntaxNode node, IReadOnlyCollection<VBSyntax.StatementSyntax> statements, bool isIterator = false, IdentifierNameSyntax csReturnVariable = null) |
| 75 | + { |
| 76 | + // _visualBasicEqualityComparison and _withBlockLhs are accessed via `this` from other partial classes |
| 77 | + var innerMethodBodyVisitor = await MethodBodyExecutableStatementVisitor.CreateAsync(node, _semanticModel, TriviaConvertingExpressionVisitor, CommonConversions, _visualBasicEqualityComparison, new Stack<ExpressionSyntax>(), _extraUsingDirectives, _typeContext, isIterator, csReturnVariable); |
| 78 | + return await GetWithConvertedGotosOrNull(statements) ?? await ConvertStatements(statements); |
| 79 | + |
| 80 | + async Task<List<StatementSyntax>> ConvertStatements(IEnumerable<VBSyntax.StatementSyntax> readOnlyCollection) |
| 81 | + { |
| 82 | + return (await readOnlyCollection.SelectManyAsync(async s => (IEnumerable<StatementSyntax>)await s.Accept(innerMethodBodyVisitor.CommentConvertingVisitor))).ToList(); |
| 83 | + } |
| 84 | + |
| 85 | + async Task<IReadOnlyCollection<StatementSyntax>> GetWithConvertedGotosOrNull(IReadOnlyCollection<VBSyntax.StatementSyntax> stmts) |
| 86 | + { |
| 87 | + var onlyIdentifierLabel = stmts.OnlyOrDefault(s => s.IsKind(VBasic.SyntaxKind.LabelStatement)); |
| 88 | + var onlyOnErrorGotoStatement = stmts.OnlyOrDefault(s => s.IsKind(VBasic.SyntaxKind.OnErrorGoToLabelStatement)); |
| 89 | + |
| 90 | + // See https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/on-error-statement |
| 91 | + if (onlyIdentifierLabel != null && onlyOnErrorGotoStatement != null) { |
| 92 | + var statementsList = stmts.ToList(); |
| 93 | + var onlyIdentifierLabelIndex = statementsList.IndexOf(onlyIdentifierLabel); |
| 94 | + var onlyOnErrorGotoStatementIndex = statementsList.IndexOf(onlyOnErrorGotoStatement); |
| 95 | + |
| 96 | + if (onlyOnErrorGotoStatementIndex < onlyIdentifierLabelIndex) { |
| 97 | + var beforeStatements = await ConvertStatements(stmts.Take(onlyOnErrorGotoStatementIndex)); |
| 98 | + var tryBlockStatements = await ConvertStatements(stmts.Take(onlyIdentifierLabelIndex).Skip(onlyOnErrorGotoStatementIndex + 1)); |
| 99 | + var tryBlock = SyntaxFactory.Block(tryBlockStatements); |
| 100 | + var afterStatements = await ConvertStatements(stmts.Skip(onlyIdentifierLabelIndex + 1)); |
| 101 | + |
| 102 | + var catchClauseSyntax = SyntaxFactory.CatchClause(); |
| 103 | + |
| 104 | + if (tryBlockStatements.LastOrDefault().IsKind(SyntaxKind.ReturnStatement)) { |
| 105 | + catchClauseSyntax = catchClauseSyntax.WithBlock(SyntaxFactory.Block(afterStatements)); |
| 106 | + afterStatements = new List<StatementSyntax>(); |
| 107 | + } |
| 108 | + |
| 109 | + var tryStatement = SyntaxFactory.TryStatement(SyntaxFactory.SingletonList(catchClauseSyntax)).WithBlock(tryBlock); |
| 110 | + return beforeStatements.Append(tryStatement).Concat(afterStatements).ToList(); |
| 111 | + } |
| 112 | + } |
| 113 | + return null; |
| 114 | + } |
| 115 | + } |
| 116 | +} |
0 commit comments