Skip to content

Commit eb4bb05

Browse files
Extract InitializerConverter
1 parent 2ad488f commit eb4bb05

2 files changed

Lines changed: 139 additions & 114 deletions

File tree

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 8 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@ namespace ICSharpCode.CodeConverter.CSharp;
1414
/// </summary>
1515
internal partial class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSharpSyntaxNode>>
1616
{
17-
private static readonly Type ConvertType = typeof(Conversions);
1817
public CommentConvertingVisitorWrapper TriviaConvertingExpressionVisitor => CommonConversions.TriviaConvertingExpressionVisitor;
1918
private readonly SemanticModel _semanticModel;
2019
private readonly HashSet<string> _extraUsingDirectives;
2120
private readonly IOperatorConverter _operatorConverter;
22-
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
2321
private readonly Stack<ExpressionSyntax> _withBlockLhs = new();
2422
private readonly ITypeContext _typeContext;
2523
private readonly QueryConverter _queryConverter;
@@ -30,6 +28,7 @@ internal partial class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<T
3028
private readonly NameExpressionNodeVisitor _nameExpressionNodeVisitor;
3129
private readonly ArgumentConverter _argumentConverter;
3230
private readonly BinaryExpressionConverter _binaryExpressionConverter;
31+
private readonly InitializerConverter _initializerConverter;
3332

3433
public ExpressionNodeVisitor(SemanticModel semanticModel,
3534
VisualBasicEqualityComparison visualBasicEqualityComparison, ITypeContext typeContext, CommonConversions commonConversions,
@@ -38,8 +37,8 @@ public ExpressionNodeVisitor(SemanticModel semanticModel,
3837
_semanticModel = semanticModel;
3938
CommonConversions = commonConversions;
4039
commonConversions.TriviaConvertingExpressionVisitor = new CommentConvertingVisitorWrapper(this, _semanticModel.SyntaxTree);
40+
_initializerConverter = new InitializerConverter(semanticModel, commonConversions, _generatedNames, _tempNameForAnonymousScope);
4141
_lambdaConverter = new LambdaConverter(commonConversions, semanticModel, _withBlockLhs, extraUsingDirectives, typeContext);
42-
_visualBasicEqualityComparison = visualBasicEqualityComparison;
4342
_queryConverter = new QueryConverter(commonConversions, _semanticModel, TriviaConvertingExpressionVisitor);
4443
_typeContext = typeContext;
4544
_extraUsingDirectives = extraUsingDirectives;
@@ -82,6 +81,12 @@ public override async Task<CSharpSyntaxNode> DefaultVisit(SyntaxNode node)
8281
public override async Task<CSharpSyntaxNode> VisitBinaryExpression(VBasic.Syntax.BinaryExpressionSyntax entryNode) => await _binaryExpressionConverter.ConvertBinaryExpressionAsync(entryNode);
8382
public override Task<CSharpSyntaxNode> VisitSingleLineLambdaExpression(VBasic.Syntax.SingleLineLambdaExpressionSyntax node) => _lambdaConverter.ConvertSingleLineLambdaAsync(node);
8483
public override Task<CSharpSyntaxNode> VisitMultiLineLambdaExpression(VBasic.Syntax.MultiLineLambdaExpressionSyntax node) => _lambdaConverter.ConvertMultiLineLambdaAsync(node);
84+
public override Task<CSharpSyntaxNode> VisitInferredFieldInitializer(VBasic.Syntax.InferredFieldInitializerSyntax node) => _initializerConverter.ConvertInferredFieldInitializerAsync(node);
85+
/// <remarks>Collection initialization has many variants in both VB and C#. Please add especially many test cases when touching this.</remarks>
86+
public override Task<CSharpSyntaxNode> VisitCollectionInitializer(VBasic.Syntax.CollectionInitializerSyntax node) => _initializerConverter.ConvertCollectionInitializerAsync(node);
87+
public override Task<CSharpSyntaxNode> VisitObjectMemberInitializer(VBasic.Syntax.ObjectMemberInitializerSyntax node) => _initializerConverter.ConvertObjectMemberInitializerAsync(node);
88+
public override Task<CSharpSyntaxNode> VisitNamedFieldInitializer(VBasic.Syntax.NamedFieldInitializerSyntax node) => _initializerConverter.ConvertNamedFieldInitializerAsync(node);
89+
public override Task<CSharpSyntaxNode> VisitObjectCollectionInitializer(VBasic.Syntax.ObjectCollectionInitializerSyntax node) => _initializerConverter.ConvertObjectCollectionInitializerAsync(node);
8590

8691
public override async Task<CSharpSyntaxNode> VisitGetTypeExpression(VBasic.Syntax.GetTypeExpressionSyntax node)
8792
{
@@ -244,11 +249,6 @@ public override async Task<CSharpSyntaxNode> VisitAnonymousObjectCreationExpress
244249

245250
}
246251

247-
public override async Task<CSharpSyntaxNode> VisitInferredFieldInitializer(VBasic.Syntax.InferredFieldInitializerSyntax node)
248-
{
249-
return SyntaxFactory.AnonymousObjectMemberDeclarator(await node.Expression.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor));
250-
}
251-
252252
public override async Task<CSharpSyntaxNode> VisitObjectCreationExpression(VBasic.Syntax.ObjectCreationExpressionSyntax node)
253253
{
254254

@@ -295,52 +295,6 @@ await initializerToConvert.AcceptAsync<InitializerExpressionSyntax>(TriviaConver
295295
);
296296
}
297297

298-
/// <remarks>Collection initialization has many variants in both VB and C#. Please add especially many test cases when touching this.</remarks>
299-
public override async Task<CSharpSyntaxNode> VisitCollectionInitializer(VBasic.Syntax.CollectionInitializerSyntax node)
300-
{
301-
var isExplicitCollectionInitializer = node.Parent is VBasic.Syntax.ObjectCollectionInitializerSyntax
302-
|| node.Parent is VBasic.Syntax.CollectionInitializerSyntax
303-
|| node.Parent is VBasic.Syntax.ArrayCreationExpressionSyntax;
304-
var initializerKind = node.IsParentKind(VBasic.SyntaxKind.ObjectCollectionInitializer) || node.IsParentKind(VBasic.SyntaxKind.ObjectCreationExpression) ?
305-
SyntaxKind.CollectionInitializerExpression :
306-
node.IsParentKind(VBasic.SyntaxKind.CollectionInitializer) && IsComplexInitializer(node) ? SyntaxKind.ComplexElementInitializerExpression :
307-
SyntaxKind.ArrayInitializerExpression;
308-
var initializers = (await node.Initializers.SelectAsync(async i => {
309-
var convertedInitializer = await i.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor);
310-
return CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(i, convertedInitializer, false);
311-
}));
312-
var initializer = SyntaxFactory.InitializerExpression(initializerKind, SyntaxFactory.SeparatedList(initializers));
313-
if (isExplicitCollectionInitializer) return initializer;
314-
315-
var convertedType = _semanticModel.GetTypeInfo(node).ConvertedType;
316-
var dimensions = convertedType is IArrayTypeSymbol ats ? ats.Rank : 1; // For multidimensional array [,] note these are different from nested arrays [][]
317-
if (!(convertedType.GetEnumerableElementTypeOrDefault() is {} elementType)) return SyntaxFactory.ImplicitArrayCreationExpression(initializer);
318-
319-
if (!initializers.Any() && dimensions == 1) {
320-
var arrayTypeArgs = SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList(CommonConversions.GetTypeSyntax(elementType)));
321-
var arrayEmpty = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
322-
ValidSyntaxFactory.IdentifierName(nameof(Array)), SyntaxFactory.GenericName(nameof(Array.Empty)).WithTypeArgumentList(arrayTypeArgs));
323-
return SyntaxFactory.InvocationExpression(arrayEmpty);
324-
}
325-
326-
bool hasExpressionToInferTypeFrom = node.Initializers.SelectMany(n => n.DescendantNodesAndSelf()).Any(n => n is not VBasic.Syntax.CollectionInitializerSyntax);
327-
if (hasExpressionToInferTypeFrom) {
328-
var commas = Enumerable.Repeat(SyntaxFactory.Token(SyntaxKind.CommaToken), dimensions - 1);
329-
return SyntaxFactory.ImplicitArrayCreationExpression(SyntaxFactory.TokenList(commas), initializer);
330-
}
331-
332-
var arrayType = (ArrayTypeSyntax)CommonConversions.CsSyntaxGenerator.ArrayTypeExpression(CommonConversions.GetTypeSyntax(elementType));
333-
var sizes = Enumerable.Repeat<ExpressionSyntax>(SyntaxFactory.OmittedArraySizeExpression(), dimensions);
334-
var arrayRankSpecifierSyntax = SyntaxFactory.SingletonList(SyntaxFactory.ArrayRankSpecifier(SyntaxFactory.SeparatedList(sizes)));
335-
arrayType = arrayType.WithRankSpecifiers(arrayRankSpecifierSyntax);
336-
return SyntaxFactory.ArrayCreationExpression(arrayType, initializer);
337-
}
338-
339-
private bool IsComplexInitializer(VBSyntax.CollectionInitializerSyntax node)
340-
{
341-
return _semanticModel.GetOperation(node.Parent.Parent) is IObjectOrCollectionInitializerOperation initializer &&
342-
initializer.Initializers.OfType<IInvocationOperation>().Any();
343-
}
344298

345299
public override async Task<CSharpSyntaxNode> VisitQueryExpression(VBasic.Syntax.QueryExpressionSyntax node)
346300
{
@@ -354,69 +308,9 @@ public override async Task<CSharpSyntaxNode> VisitOrdering(VBasic.Syntax.Orderin
354308
var ascendingOrDescendingKeyword = node.AscendingOrDescendingKeyword.ConvertToken();
355309
return SyntaxFactory.Ordering(convertToken, expressionSyntax, ascendingOrDescendingKeyword);
356310
}
357-
358-
public override async Task<CSharpSyntaxNode> VisitObjectMemberInitializer(VBasic.Syntax.ObjectMemberInitializerSyntax node)
359-
{
360-
var initializers = await node.Initializers.AcceptSeparatedListAsync<VBSyntax.FieldInitializerSyntax, ExpressionSyntax>(TriviaConvertingExpressionVisitor);
361-
return SyntaxFactory.InitializerExpression(SyntaxKind.ObjectInitializerExpression, initializers);
362-
}
363-
364-
public override async Task<CSharpSyntaxNode> VisitNamedFieldInitializer(VBasic.Syntax.NamedFieldInitializerSyntax node)
365-
{
366-
var csExpressionSyntax = await node.Expression.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor);
367-
csExpressionSyntax =
368-
CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, csExpressionSyntax);
369-
if (node.Parent?.Parent is VBasic.Syntax.AnonymousObjectCreationExpressionSyntax {Initializer: {Initializers: var initializers}} anonymousObjectCreationExpression) {
370-
string nameIdentifierText = node.Name.Identifier.Text;
371-
var isAnonymouslyReused = initializers.OfType<VBasic.Syntax.NamedFieldInitializerSyntax>()
372-
.Select(i => i.Expression).OfType<VBasic.Syntax.MemberAccessExpressionSyntax>()
373-
.Any(maes => maes.Expression is null && maes.Name.Identifier.Text.Equals(nameIdentifierText, StringComparison.OrdinalIgnoreCase));
374-
if (isAnonymouslyReused) {
375-
string tempNameForAnonymousSelfReference = GenerateUniqueVariableName(node.Name, "temp" + ((VBSyntax.SimpleNameSyntax) node.Name).Identifier.Text.UppercaseFirstLetter());
376-
csExpressionSyntax = DeclareVariableInline(csExpressionSyntax, tempNameForAnonymousSelfReference);
377-
if (!_tempNameForAnonymousScope.TryGetValue(nameIdentifierText, out var stack)) {
378-
stack = _tempNameForAnonymousScope[nameIdentifierText] = new Stack<(SyntaxNode Scope, string TempName)>();
379-
}
380-
stack.Push((anonymousObjectCreationExpression, tempNameForAnonymousSelfReference));
381-
}
382-
383-
var anonymousObjectMemberDeclaratorSyntax = SyntaxFactory.AnonymousObjectMemberDeclarator(
384-
SyntaxFactory.NameEquals(SyntaxFactory.IdentifierName(ConvertIdentifier(node.Name.Identifier))),
385-
csExpressionSyntax);
386-
return anonymousObjectMemberDeclaratorSyntax;
387-
}
388-
389-
return SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
390-
await node.Name.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor),
391-
csExpressionSyntax
392-
);
393-
}
394-
395-
private string GenerateUniqueVariableName(VisualBasicSyntaxNode existingNode, string varNameBase) => NameGenerator.CS.GetUniqueVariableNameInScope(_semanticModel, _generatedNames, existingNode, varNameBase);
396-
397-
private static ExpressionSyntax DeclareVariableInline(ExpressionSyntax csExpressionSyntax, string temporaryName)
398-
{
399-
var temporaryNameId = SyntaxFactory.Identifier(temporaryName);
400-
var temporaryNameExpression = ValidSyntaxFactory.IdentifierName(temporaryNameId);
401-
csExpressionSyntax = SyntaxFactory.ConditionalExpression(
402-
SyntaxFactory.IsPatternExpression(
403-
csExpressionSyntax,
404-
SyntaxFactory.VarPattern(
405-
SyntaxFactory.SingleVariableDesignation(temporaryNameId))),
406-
temporaryNameExpression,
407-
SyntaxFactory.LiteralExpression(
408-
SyntaxKind.DefaultLiteralExpression,
409-
SyntaxFactory.Token(SyntaxKind.DefaultKeyword)));
410-
return csExpressionSyntax;
411-
}
412-
413311
public override async Task<CSharpSyntaxNode> VisitVariableNameEquals(VBSyntax.VariableNameEqualsSyntax node) =>
414312
SyntaxFactory.NameEquals(SyntaxFactory.IdentifierName(ConvertIdentifier(node.Identifier.Identifier)));
415313

416-
public override async Task<CSharpSyntaxNode> VisitObjectCollectionInitializer(VBasic.Syntax.ObjectCollectionInitializerSyntax node)
417-
{
418-
return await node.Initializer.AcceptAsync<CSharpSyntaxNode>(TriviaConvertingExpressionVisitor); //Dictionary initializer comes through here despite the FROM keyword not being in the source code
419-
}
420314

421315
public override async Task<CSharpSyntaxNode> VisitBinaryConditionalExpression(VBasic.Syntax.BinaryConditionalExpressionSyntax node)
422316
{

0 commit comments

Comments
 (0)