Skip to content

Commit 37a3dd6

Browse files
Add as constructor initializer
1 parent a339291 commit 37a3dd6

File tree

8 files changed

+72
-31
lines changed

8 files changed

+72
-31
lines changed

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
* Fix logic issue when converting property passed byref - [#324](https://github.com/icsharpcode/CodeConverter/issues/324)
1818
* Fix logic issue when converting expression passed in byref within conditional expression - [#310](https://github.com/icsharpcode/CodeConverter/issues/310)
1919
* Added constructors now only added to the relevant type - not other types in the same file
20+
* Converted field initializers moved to constructor in most cases - [#281](https://github.com/icsharpcode/CodeConverter/issues/281)
2021

2122
### C# -> VB
2223

CodeConverter/CSharp/AdditionalInitializers.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ namespace ICSharpCode.CodeConverter.CSharp
1111
{
1212
public class AdditionalInitializers
1313
{
14+
public AdditionalInitializers(bool shouldAddTypeWideInitToThisPart)
15+
{
16+
ShouldAddTypeWideInitToThisPart = shouldAddTypeWideInitToThisPart;
17+
}
18+
1419
public List<(ExpressionSyntax Field, SyntaxKind AssignmentKind, ExpressionSyntax Initializer)> AdditionalStaticInitializers { get; } = new List<(ExpressionSyntax, SyntaxKind, ExpressionSyntax)>();
1520
public List<(ExpressionSyntax Field, SyntaxKind AssignmentKind, ExpressionSyntax Initializer)> AdditionalInstanceInitializers { get; } = new List<(ExpressionSyntax, SyntaxKind, ExpressionSyntax)>();
21+
public bool ShouldAddTypeWideInitToThisPart { get; }
1622

1723
public IReadOnlyCollection<MemberDeclarationSyntax> WithAdditionalInitializers(ITypeSymbol parentType,
18-
List<MemberDeclarationSyntax> convertedMembers, SyntaxToken parentTypeName, bool shouldAddTypeWideInitToThisPart, bool requiresInitializeComponent)
24+
List<MemberDeclarationSyntax> convertedMembers, SyntaxToken parentTypeName, bool requiresInitializeComponent)
1925
{
2026
var constructorsInAllParts = parentType?.GetMembers().OfType<IMethodSymbol>().Where(m => m.IsConstructor()).ToList();
2127
var parameterlessConstructorsInAllParts = constructorsInAllParts?.Where(c => !c.IsImplicitlyDeclared && !c.Parameters.Any()) ?? Array.Empty<IMethodSymbol>();
@@ -25,7 +31,7 @@ public IReadOnlyCollection<MemberDeclarationSyntax> WithAdditionalInitializers(I
2531
.Where(cds => !cds.Initializer.IsKind(SyntaxKind.ThisConstructorInitializer))
2632
.ToLookup(cds => cds.IsInStaticCsContext());
2733

28-
convertedMembers = WithAdditionalInitializers(convertedMembers, parentTypeName, AdditionalInstanceInitializers, SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)), rootConstructors[false], shouldAddTypeWideInitToThisPart && requiresInstanceConstructor, requiresInitializeComponent);
34+
convertedMembers = WithAdditionalInitializers(convertedMembers, parentTypeName, AdditionalInstanceInitializers, SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword)), rootConstructors[false], ShouldAddTypeWideInitToThisPart && requiresInstanceConstructor, requiresInitializeComponent);
2935

3036
convertedMembers = WithAdditionalInitializers(convertedMembers, parentTypeName,
3137
AdditionalStaticInitializers, SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.StaticKeyword)), rootConstructors[true], requiresStaticConstructor, false);

CodeConverter/CSharp/CommonConversions.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,21 @@ internal class CommonConversions
4343
private readonly SemanticModel _semanticModel;
4444
public SyntaxGenerator CsSyntaxGenerator { get; }
4545
private readonly CSharpCompilation _csCompilation;
46+
private readonly ITypeContext _typeContext;
47+
4648
public CommentConvertingVisitorWrapper TriviaConvertingExpressionVisitor { get; set; }
4749
public TypeConversionAnalyzer TypeConversionAnalyzer { get; }
4850

4951
public CommonConversions(Document document, SemanticModel semanticModel,
5052
TypeConversionAnalyzer typeConversionAnalyzer, SyntaxGenerator csSyntaxGenerator,
51-
CSharpCompilation csCompilation)
53+
CSharpCompilation csCompilation, ITypeContext typeContext)
5254
{
5355
TypeConversionAnalyzer = typeConversionAnalyzer;
5456
Document = document;
5557
_semanticModel = semanticModel;
5658
CsSyntaxGenerator = csSyntaxGenerator;
5759
_csCompilation = csCompilation;
60+
_typeContext = typeContext;
5861
}
5962

6063
public async Task<(IReadOnlyCollection<(VariableDeclarationSyntax Decl, ITypeSymbol Type)> Variables, IReadOnlyCollection<CSharpSyntaxNode> Methods)> SplitVariableDeclarations(
@@ -136,7 +139,16 @@ private async Task<EqualsValueClauseSyntax> ConvertEqualsValueClauseSyntax(
136139
var convertedInitializer = vbInitValue != null
137140
? TypeConversionAnalyzer.AddExplicitConversion(vbInitValue, adjustedInitializerExpr, isConst: declaredConst)
138141
: adjustedInitializerExpr;
139-
equalsValueClauseSyntax = SyntaxFactory.EqualsValueClause(convertedInitializer);
142+
143+
if (isField && !declaredSymbol.IsStatic && !IsDefinitelyStatic(vbName, vbInitValue)) {
144+
//TODO If not the part where we're doing type wide init, convert this declaration into an arrow property called "initial<FieldName>", then call it from the other part
145+
// Assuming this is the part for type wide init
146+
var lhs = SyntaxFactory.IdentifierName(ConvertIdentifier(vbName.Identifier, sourceTriviaMapKind: SourceTriviaMapKind.None));
147+
_typeContext.Initializers.AdditionalInstanceInitializers.Add((lhs, CSSyntaxKind.SimpleAssignmentExpression, adjustedInitializerExpr));
148+
equalsValueClauseSyntax = null;
149+
} else {
150+
equalsValueClauseSyntax = SyntaxFactory.EqualsValueClause(convertedInitializer);
151+
}
140152
}
141153
else if (isField || declaredSymbol != null && _semanticModel.IsDefinitelyAssignedBeforeRead(declaredSymbol, vbName))
142154
{
@@ -151,6 +163,24 @@ private async Task<EqualsValueClauseSyntax> ConvertEqualsValueClauseSyntax(
151163
return equalsValueClauseSyntax;
152164
}
153165

166+
/// <summary>
167+
/// Returns true only if expressions static (i.e. doesn't reference the containing instance)
168+
/// </summary>
169+
private bool IsDefinitelyStatic(ModifiedIdentifierSyntax vbName, VBSyntax.ExpressionSyntax vbInitValue)
170+
{
171+
var arrayBoundExpressions = vbName.ArrayBounds != null ? vbName.ArrayBounds.Arguments.Select(a => a.GetExpression()).Where(x => x != null) : Enumerable.Empty<VBSyntax.ExpressionSyntax>();
172+
var expressions = vbInitValue.Yield().Concat(arrayBoundExpressions).ToArray();
173+
return expressions.All(IsDefinitelyStatic);
174+
}
175+
176+
/// <summary>
177+
/// Returns true only if expression is static (i.e. doesn't reference the containing instance)
178+
/// </summary>
179+
private bool IsDefinitelyStatic(VBSyntax.ExpressionSyntax e)
180+
{
181+
return _semanticModel.GetOperation(e).DescendantsAndSelf().OfType<IInstanceReferenceOperation>().Any() == false;
182+
}
183+
154184
private VariableDeclarationSyntax CreateVariableDeclaration(VariableDeclaratorSyntax vbDeclarator, bool preferExplicitType,
155185
bool requireExplicitTypeForAll, ITypeSymbol vbInitializerType, ITypeSymbol declaredSymbolType,
156186
EqualsValueClauseSyntax equalsValueClauseSyntax, IMethodSymbol initSymbol, CSSyntax.VariableDeclaratorSyntax v)

CodeConverter/CSharp/DeclarationNodeVisitor.cs

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public DeclarationNodeVisitor(Document document, Compilation compilation, Semant
5959
TriviaConvertingDeclarationVisitor = new CommentConvertingVisitorWrapper(this, _semanticModel.SyntaxTree);
6060
var expressionEvaluator = new ExpressionEvaluator(semanticModel, _visualBasicEqualityComparison);
6161
var typeConversionAnalyzer = new TypeConversionAnalyzer(semanticModel, csCompilation, _extraUsingDirectives, _csSyntaxGenerator, expressionEvaluator);
62-
CommonConversions = new CommonConversions(document, semanticModel, typeConversionAnalyzer, csSyntaxGenerator, csCompilation);
62+
CommonConversions = new CommonConversions(document, semanticModel, typeConversionAnalyzer, csSyntaxGenerator, csCompilation, _typeContext);
6363
var expressionNodeVisitor = new ExpressionNodeVisitor(semanticModel, _visualBasicEqualityComparison, _additionalLocals, csCompilation, _typeContext, CommonConversions, _extraUsingDirectives);
6464
_triviaConvertingExpressionVisitor = expressionNodeVisitor.TriviaConvertingExpressionVisitor;
6565
_createMethodBodyVisitorAsync = expressionNodeVisitor.CreateMethodBodyVisitor;
@@ -188,20 +188,19 @@ private async Task<string> WithDeclarationCasing(VBSyntax.NamespaceBlockSyntax n
188188
return namespaceToDeclare;
189189
}
190190

191-
#region Namespace Members
192-
193191
private async Task<IEnumerable<MemberDeclarationSyntax>> ConvertMembers(VBSyntax.TypeBlockSyntax parentType)
194192
{
195193
var members = parentType.Members;
196-
var additionalInitializers = new AdditionalInitializers();
194+
195+
var namedTypeSymbol = _semanticModel.GetDeclaredSymbol(parentType);
196+
bool shouldAddTypeWideInitToThisPart = ShouldAddTypeWideInitToThisPart(parentType, namedTypeSymbol);
197+
var additionalInitializers = new AdditionalInitializers(shouldAddTypeWideInitToThisPart);
197198
var methodsWithHandles = MethodsWithHandles.Create(GetMethodWithHandles(parentType));
198199

199200
if (methodsWithHandles.Any()) _extraUsingDirectives.Add("System.Runtime.CompilerServices");//For MethodImplOptions.Synchronized
200201

201202
IEnumerable<MemberDeclarationSyntax> directlyConvertedMembers = await GetDirectlyConvertedMembers(additionalInitializers, methodsWithHandles);
202203

203-
var namedTypeSymbol = _semanticModel.GetDeclaredSymbol(parentType);
204-
bool shouldAddTypeWideInitToThisPart = ShouldAddTypeWideInitToThisPart(parentType, namedTypeSymbol);
205204
var requiresInitializeComponent = namedTypeSymbol.IsDesignerGeneratedTypeWithInitializeComponent(_compilation);
206205

207206
if (shouldAddTypeWideInitToThisPart) {
@@ -213,7 +212,7 @@ private async Task<IEnumerable<MemberDeclarationSyntax>> ConvertMembers(VBSyntax
213212
}
214213
}
215214

216-
return additionalInitializers.WithAdditionalInitializers(namedTypeSymbol, directlyConvertedMembers.ToList(), CommonConversions.ConvertIdentifier(parentType.BlockStatement.Identifier), shouldAddTypeWideInitToThisPart, requiresInitializeComponent);
215+
return additionalInitializers.WithAdditionalInitializers(namedTypeSymbol, directlyConvertedMembers.ToList(), CommonConversions.ConvertIdentifier(parentType.BlockStatement.Identifier), requiresInitializeComponent);
217216

218217
async Task<MemberDeclarationSyntax[]> GetDirectlyConvertedMembers(AdditionalInitializers additionalInitializers, MethodsWithHandles methodsWithHandles)
219218
{
@@ -441,18 +440,14 @@ public override async Task<CSharpSyntaxNode> VisitDelegateStatement(VBSyntax.Del
441440
);
442441
}
443442

444-
#endregion
445-
446-
#region Type Members
447-
448443
public override async Task<CSharpSyntaxNode> VisitFieldDeclaration(VBSyntax.FieldDeclarationSyntax node)
449444
{
450445
_additionalLocals.PushScope();
451446
List<MemberDeclarationSyntax> declarations;
452447
try {
453448
declarations = await GetMemberDeclarations(node);
454449
} finally {
455-
_additionalLocals.PopScope();
450+
_additionalLocals.PopExpressionScope();
456451
}
457452
_additionalDeclarations.Add(node, declarations.Skip(1).ToArray());
458453
return declarations.First();
@@ -1230,9 +1225,6 @@ public override async Task<CSharpSyntaxNode> VisitTypeParameterList(VBSyntax.Typ
12301225
);
12311226
}
12321227

1233-
#endregion
1234-
1235-
12361228
private async Task<(TypeParameterListSyntax parameters, SyntaxList<TypeParameterConstraintClauseSyntax> constraints)> SplitTypeParameters(VBSyntax.TypeParameterListSyntax typeParameterList)
12371229
{
12381230
var constraints = SyntaxFactory.List<TypeParameterConstraintClauseSyntax>();

CodeConverter/CSharp/ExpressionEvaluator.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Linq;
43
using System.Reflection;
54
using ICSharpCode.CodeConverter.Util;
6-
using ICSharpCode.CodeConverter.Util.FromRoslyn;
75
using Microsoft.CodeAnalysis;
8-
using Microsoft.CodeAnalysis.CSharp;
9-
using Microsoft.CodeAnalysis.Editing;
10-
using Microsoft.CodeAnalysis.FindSymbols;
116
using Microsoft.CodeAnalysis.Operations;
12-
using Microsoft.CodeAnalysis.VisualBasic;
137
using Microsoft.VisualBasic;
148
using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax;
159
using Microsoft.VisualBasic.CompilerServices;
1610
using Microsoft.CodeAnalysis.CSharp.Syntax;
17-
using System.Linq.Expressions;
1811

1912
namespace ICSharpCode.CodeConverter.CSharp
2013
{

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ internal class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSha
3636
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
3737
private readonly Stack<ExpressionSyntax> _withBlockLhs = new Stack<ExpressionSyntax>();
3838
private readonly HoistedNodeState _additionalLocals;
39-
private readonly TypeContext _typeContext;
39+
private readonly ITypeContext _typeContext;
4040
private readonly QueryConverter _queryConverter;
4141
private readonly Lazy<IDictionary<ITypeSymbol, string>> _convertMethodsLookupByReturnType;
4242
private readonly Compilation _csCompilation;
@@ -45,7 +45,7 @@ internal class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSha
4545

4646
public ExpressionNodeVisitor(SemanticModel semanticModel,
4747
VisualBasicEqualityComparison visualBasicEqualityComparison, HoistedNodeState additionalLocals,
48-
Compilation csCompilation, TypeContext typeContext, CommonConversions commonConversions,
48+
Compilation csCompilation, ITypeContext typeContext, CommonConversions commonConversions,
4949
HashSet<string> extraUsingDirectives)
5050
{
5151
CommonConversions = commonConversions;

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ internal class MethodBodyExecutableStatementVisitor : VBasic.VisualBasicSyntaxVi
4141

4242
private CommonConversions CommonConversions { get; }
4343

44-
public static async Task<MethodBodyExecutableStatementVisitor> CreateAsync(VBasic.VisualBasicSyntaxNode node, SemanticModel semanticModel, CommentConvertingVisitorWrapper triviaConvertingExpressionVisitor, CommonConversions commonConversions, Stack<ExpressionSyntax> withBlockLhs, HashSet<string> extraUsingDirectives, HoistedNodeState additionalLocals, TypeContext typeContext, bool isIterator, IdentifierNameSyntax csReturnVariable)
44+
public static async Task<MethodBodyExecutableStatementVisitor> CreateAsync(VBasic.VisualBasicSyntaxNode node, SemanticModel semanticModel, CommentConvertingVisitorWrapper triviaConvertingExpressionVisitor, CommonConversions commonConversions, Stack<ExpressionSyntax> withBlockLhs, HashSet<string> extraUsingDirectives, HoistedNodeState additionalLocals, ITypeContext typeContext, bool isIterator, IdentifierNameSyntax csReturnVariable)
4545
{
4646
var solution = commonConversions.Document.Project.Solution;
4747
var declarationsToInlineInLoop = await solution.GetDescendantsToInlineInLoopAsync(semanticModel, node);
@@ -54,7 +54,7 @@ public static async Task<MethodBodyExecutableStatementVisitor> CreateAsync(VBasi
5454
private MethodBodyExecutableStatementVisitor(VBasic.VisualBasicSyntaxNode methodNode, SemanticModel semanticModel,
5555
CommentConvertingVisitorWrapper expressionVisitor, CommonConversions commonConversions,
5656
Stack<ExpressionSyntax> withBlockLhs, HashSet<string> extraUsingDirectives,
57-
HoistedNodeState additionalLocals, TypeContext typeContext, HashSet<ILocalSymbol> localsToInlineInLoop)
57+
HoistedNodeState additionalLocals, ITypeContext typeContext, HashSet<ILocalSymbol> localsToInlineInLoop)
5858
{
5959
_methodNode = methodNode;
6060
_semanticModel = semanticModel;

Tests/CSharp/MemberTests/MemberTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,25 @@ internal partial class TestClass
719719
}");
720720
}
721721

722+
[Fact]
723+
public async Task FieldWithNonStaticInitializerAsync()
724+
{
725+
await TestConversionVisualBasicToCSharpAsync(@"Public Class A
726+
Private x As Integer = 2
727+
Private y(x) As Integer
728+
End Class", @"
729+
public partial class A
730+
{
731+
public A()
732+
{
733+
y = new int[x + 1];
734+
}
735+
736+
private int x = 2;
737+
private int[] y;
738+
}");
739+
}
740+
722741
[Fact]
723742
public async Task ParamArrayAsync()
724743
{

0 commit comments

Comments
 (0)