Skip to content

Commit c35bf3f

Browse files
Use explicit type as default for array creation - fixes #713
1 parent 73c279f commit c35bf3f

6 files changed

Lines changed: 95 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1717
* Keep optional parameters for parameterized properties [#642](https://github.com/icsharpcode/CodeConverter/issues/642)
1818
* Maintain leading whitespace in comments [#711](https://github.com/icsharpcode/CodeConverter/issues/711)
1919
* Avoid XmlException when referencing certain nuget packages [#714](https://github.com/icsharpcode/CodeConverter/issues/714)
20+
* Use explicit type as default for array creation [#713](https://github.com/icsharpcode/CodeConverter/issues/713)
2021

2122
### C# -> VB
2223

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@ await initializerToConvert.AcceptAsync<InitializerExpressionSyntax>(TriviaConver
575575
);
576576
}
577577

578+
/// <remarks>Collection initialization has many variants in both VB and C#. Please add especially many test cases when touching this.</remarks>
578579
public override async Task<CSharpSyntaxNode> VisitCollectionInitializer(VBasic.Syntax.CollectionInitializerSyntax node)
579580
{
580581
var isExplicitCollectionInitializer = node.Parent is VBasic.Syntax.ObjectCollectionInitializerSyntax
@@ -591,17 +592,28 @@ public override async Task<CSharpSyntaxNode> VisitCollectionInitializer(VBasic.S
591592
var initializer = SyntaxFactory.InitializerExpression(initializerKind, SyntaxFactory.SeparatedList(initializers));
592593
if (isExplicitCollectionInitializer) return initializer;
593594

594-
if (!(_semanticModel.GetTypeInfo(node).ConvertedType is IArrayTypeSymbol arrayType)) return SyntaxFactory.ImplicitArrayCreationExpression(initializer);
595-
596-
if (!initializers.Any() && arrayType.Rank == 1) {
597-
598-
var arrayTypeArgs = SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList(CommonConversions.GetTypeSyntax(arrayType.ElementType)));
595+
var convertedType = _semanticModel.GetTypeInfo(node).ConvertedType;
596+
var dimensions = convertedType is IArrayTypeSymbol ats ? ats.Rank : 1; // For multidimensional array [,] note these are different from nested arrays [][]
597+
if (!(convertedType.GetEnumerableElementTypeOrDefault() is {} elementType)) return SyntaxFactory.ImplicitArrayCreationExpression(initializer);
598+
599+
if (!initializers.Any() && dimensions == 1) {
600+
var arrayTypeArgs = SyntaxFactory.TypeArgumentList(SyntaxFactory.SingletonSeparatedList(CommonConversions.GetTypeSyntax(elementType)));
599601
var arrayEmpty = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
600602
SyntaxFactory.IdentifierName(nameof(Array)), SyntaxFactory.GenericName(nameof(Array.Empty)).WithTypeArgumentList(arrayTypeArgs));
601603
return SyntaxFactory.InvocationExpression(arrayEmpty);
602604
}
603-
var commas = Enumerable.Repeat(SyntaxFactory.Token(SyntaxKind.CommaToken), arrayType.Rank - 1);
604-
return SyntaxFactory.ImplicitArrayCreationExpression(SyntaxFactory.TokenList(commas), initializer);
605+
606+
bool hasExpressionToInferTypeFrom = node.Initializers.SelectMany(n => n.DescendantNodesAndSelf()).Any(n => n is not VBasic.Syntax.CollectionInitializerSyntax);
607+
if (hasExpressionToInferTypeFrom) {
608+
var commas = Enumerable.Repeat(SyntaxFactory.Token(SyntaxKind.CommaToken), dimensions - 1);
609+
return SyntaxFactory.ImplicitArrayCreationExpression(SyntaxFactory.TokenList(commas), initializer);
610+
}
611+
612+
var arrayType = (ArrayTypeSyntax)CommonConversions.CsSyntaxGenerator.ArrayTypeExpression(CommonConversions.GetTypeSyntax(elementType));
613+
var sizes = Enumerable.Repeat<ExpressionSyntax>(SyntaxFactory.OmittedArraySizeExpression(), dimensions);
614+
var arrayRankSpecifierSyntax = SyntaxFactory.SingletonList(SyntaxFactory.ArrayRankSpecifier(SyntaxFactory.SeparatedList(sizes)));
615+
arrayType = arrayType.WithRankSpecifiers(arrayRankSpecifierSyntax);
616+
return SyntaxFactory.ArrayCreationExpression(arrayType, initializer);
605617
}
606618

607619
private bool IsComplexInitializer(VBSyntax.CollectionInitializerSyntax node)

CodeConverter/CodeConverter.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ See https://github.com/icsharpcode/CodeConverter for a pre-built command line, v
2424
<PackageTags>Convert Converter Conversion C# CSharp CS VB VB.NET Visual Basic Code Free Roslyn Tool</PackageTags>
2525
<PackageReleaseNotes>See https://github.com/icsharpcode/CodeConverter/blob/master/CHANGELOG.md </PackageReleaseNotes>
2626
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
27-
<LangVersion>8.0</LangVersion>
27+
<LangVersion>9.0</LangVersion>
2828
<NoWarn>$(NoWarn);1998</NoWarn>
2929
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
3030
</PropertyGroup>

CodeConverter/Util/ITypeSymbolExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public static bool IsEnumerableOfExactType(this ITypeSymbol symbol, ITypeSymbol
5656
return SymbolEquivalenceComparer.Instance.Equals(GetEnumerableElementTypeOrDefault(symbol), typeArg);
5757
}
5858

59-
private static ITypeSymbol GetEnumerableElementTypeOrDefault(ITypeSymbol symbol)
59+
public static ITypeSymbol GetEnumerableElementTypeOrDefault(this ITypeSymbol symbol)
6060
{
6161
if (symbol is IArrayTypeSymbol ats) return ats.ElementType;
6262
if (symbol is INamedTypeSymbol nt) return nt.Yield().Concat(nt.AllInterfaces).OfType<INamedTypeSymbol>()

Tests/CSharp/ExpressionTests/ExpressionTests.cs

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,21 +475,88 @@ public void Test()
475475
public async Task EmptyArrayExpressionAsync()
476476
{
477477
await TestConversionVisualBasicToCSharpAsync(@"
478-
Public Class Issue495
478+
Public Class Issue495AndIssue713
479479
Public Function Empty() As Integer()
480+
Dim emptySingle As IEnumerable(Of Integer) = {}
481+
Dim initializedSingle As IEnumerable(Of Integer) = {1}
482+
Dim emptyNested As Integer()() = {}
483+
Dim initializedNested(1)() As Integer
484+
Dim empty2d As Integer(,) = {{}}
485+
Dim initialized2d As Integer(,) = {{1}}
480486
Return {}
481487
End Function
482488
End Class", @"using System;
489+
using System.Collections.Generic;
490+
491+
public partial class Issue495AndIssue713
492+
{
493+
public int[] Empty()
494+
{
495+
IEnumerable<int> emptySingle = Array.Empty<int>();
496+
IEnumerable<int> initializedSingle = new[] { 1 };
497+
var emptyNested = Array.Empty<int[]>();
498+
var initializedNested = new int[2][];
499+
var empty2d = new int[,] { { } };
500+
var initialized2d = new[,] { { 1 } };
501+
return Array.Empty<int>();
502+
}
503+
}");
504+
}
505+
506+
[Fact]
507+
public async Task InitializedArrayExpressionAsync()
508+
{
509+
await TestConversionVisualBasicToCSharpAsync(@"
510+
Public Class Issue713
511+
Public Function Empty() As Integer()
512+
Dim initializedSingle As IEnumerable(Of Integer) = {1}
513+
Dim initialized2d As Integer(,) = {{1}}
514+
Return {}
515+
End Function
516+
End Class", @"using System;
517+
using System.Collections.Generic;
483518
484-
public partial class Issue495
519+
public partial class Issue713
485520
{
486521
public int[] Empty()
487522
{
523+
IEnumerable<int> initializedSingle = new[] { 1 };
524+
var initialized2d = new[,] { { 1 } };
488525
return Array.Empty<int>();
489526
}
490527
}");
491528
}
492529

530+
[Fact]
531+
public async Task EmptyArrayParameterAsync()
532+
{
533+
await TestConversionVisualBasicToCSharpAsync(@"Public Class VisualBasicClass
534+
Public Sub s()
535+
If Validate({}) Then
536+
End If
537+
End Sub
538+
Private Function Validate(w As IEnumerable(Of Int16)) As Boolean
539+
Return True
540+
End Function
541+
End Class", @"using System;
542+
using System.Collections.Generic;
543+
544+
public partial class VisualBasicClass
545+
{
546+
public void s()
547+
{
548+
if (Validate(Array.Empty<short>()))
549+
{
550+
}
551+
}
552+
553+
private bool Validate(IEnumerable<short> w)
554+
{
555+
return true;
556+
}
557+
}");
558+
}
559+
493560
[Fact]
494561
public async Task Empty2DArrayExpressionAsync()
495562
{

Tests/CSharp/StatementTests/StatementTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -750,14 +750,14 @@ public async Task ArrayInitializationStatementAsync()
750750
{
751751
await TestConversionVisualBasicToCSharpAsync(@"Class TestClass
752752
Private Sub TestMethod()
753-
Dim b As Integer() = {1, 2, 3}
753+
Dim b As Integer() = {1, 2, 4}
754754
End Sub
755755
End Class", @"
756756
internal partial class TestClass
757757
{
758758
private void TestMethod()
759759
{
760-
var b = new[] { 1, 2, 3 };
760+
var b = new[] { 1, 2, 4 };
761761
}
762762
}");
763763
}
@@ -956,14 +956,14 @@ public async Task MultidimensionalArrayInitializationStatementWithTypeAsync()
956956
{
957957
await TestConversionVisualBasicToCSharpAsync(@"Class TestClass
958958
Private Sub TestMethod()
959-
Dim b As Integer(,) = New Integer(,) {{1, 2}, {3, 4}}
959+
Dim b As Integer(,) = New Integer(,) {{1, 3}, {2, 4}}
960960
End Sub
961961
End Class", @"
962962
internal partial class TestClass
963963
{
964964
private void TestMethod()
965965
{
966-
var b = new int[,] { { 1, 2 }, { 3, 4 } };
966+
var b = new int[,] { { 1, 3 }, { 2, 4 } };
967967
}
968968
}");
969969
}

0 commit comments

Comments
 (0)