Skip to content

Commit 1a8c5c8

Browse files
Use stack so nested types get correct initializers
1 parent fda0685 commit 1a8c5c8

File tree

8 files changed

+79
-42
lines changed

8 files changed

+79
-42
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1010

1111
### VB -> C#
1212

13-
* Improve multi-declaration field conversion for arrrays - [#559](https://github.com/icsharpcode/CodeConverter/issues/559)
13+
* Improve multi-declaration field conversion for arrays - [#559](https://github.com/icsharpcode/CodeConverter/issues/559)
1414
* Add parentheses around ternary statement - [#565](https://github.com/icsharpcode/CodeConverter/issues/565)
1515
* When converting ForEach loop, avoid duplicate variable compilation issue [#558](https://github.com/icsharpcode/CodeConverter/issues/558)
1616
* Improvements to for loop with missing semantic info - [#482](https://github.com/icsharpcode/CodeConverter/issues/482)
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)
19+
* Added constructors now only added to the relevant type - not other types in the same file
1920

2021
### C# -> VB
2122

CodeConverter/CSharp/DeclarationNodeVisitor.cs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
namespace ICSharpCode.CodeConverter.CSharp
2121
{
22+
2223
/// <summary>
2324
/// Declaration nodes, and nodes only used directly in that declaration (i.e. never within an expression)
2425
/// e.g. Class, Enum, TypeConstraint
@@ -33,18 +34,15 @@ internal class DeclarationNodeVisitor : VBasic.VisualBasicSyntaxVisitor<Task<CSh
3334
private readonly SyntaxGenerator _csSyntaxGenerator;
3435
private readonly Compilation _compilation;
3536
private readonly SemanticModel _semanticModel;
36-
private readonly MethodsWithHandles _methodsWithHandles = new MethodsWithHandles();
3737
private readonly Dictionary<VBSyntax.StatementSyntax, MemberDeclarationSyntax[]> _additionalDeclarations = new Dictionary<VBSyntax.StatementSyntax, MemberDeclarationSyntax[]>();
38-
private readonly AdditionalInitializers _additionalInitializers;
38+
private readonly TypeContext _typeContext = new TypeContext();
3939
private readonly HoistedNodeState _additionalLocals = new HoistedNodeState();
4040
private uint _failedMemberConversionMarkerCount;
4141
private readonly HashSet<string> _extraUsingDirectives = new HashSet<string>();
4242
private readonly VisualBasicEqualityComparison _visualBasicEqualityComparison;
4343
private HashSet<string> _accessedThroughMyClass;
4444
public CommentConvertingVisitorWrapper TriviaConvertingDeclarationVisitor { get; }
4545
private readonly CommentConvertingVisitorWrapper _triviaConvertingExpressionVisitor;
46-
47-
4846
private string _topAncestorNamespace;
4947

5048
private CommonConversions CommonConversions { get; }
@@ -62,8 +60,7 @@ public DeclarationNodeVisitor(Document document, Compilation compilation, Semant
6260
var expressionEvaluator = new ExpressionEvaluator(semanticModel, _visualBasicEqualityComparison);
6361
var typeConversionAnalyzer = new TypeConversionAnalyzer(semanticModel, csCompilation, _extraUsingDirectives, _csSyntaxGenerator, expressionEvaluator);
6462
CommonConversions = new CommonConversions(document, semanticModel, typeConversionAnalyzer, csSyntaxGenerator, csCompilation);
65-
_additionalInitializers = new AdditionalInitializers();
66-
var expressionNodeVisitor = new ExpressionNodeVisitor(semanticModel, _visualBasicEqualityComparison, _additionalLocals, csCompilation, _methodsWithHandles, CommonConversions, _extraUsingDirectives);
63+
var expressionNodeVisitor = new ExpressionNodeVisitor(semanticModel, _visualBasicEqualityComparison, _additionalLocals, csCompilation, _typeContext, CommonConversions, _extraUsingDirectives);
6764
_triviaConvertingExpressionVisitor = expressionNodeVisitor.TriviaConvertingExpressionVisitor;
6865
_createMethodBodyVisitorAsync = expressionNodeVisitor.CreateMethodBodyVisitor;
6966
CommonConversions.TriviaConvertingExpressionVisitor = _triviaConvertingExpressionVisitor;
@@ -196,15 +193,12 @@ private async Task<string> WithDeclarationCasing(VBSyntax.NamespaceBlockSyntax n
196193
private async Task<IEnumerable<MemberDeclarationSyntax>> ConvertMembers(VBSyntax.TypeBlockSyntax parentType)
197194
{
198195
var members = parentType.Members;
196+
var additionalInitializers = new AdditionalInitializers();
197+
var methodsWithHandles = MethodsWithHandles.Create(GetMethodWithHandles(parentType));
199198

200-
//TODO: Store these per-type so nested classes aren't affected
201-
_methodsWithHandles.Initialize(GetMethodWithHandles(parentType));
202-
_additionalInitializers.AdditionalInstanceInitializers.Clear();
203-
_additionalInitializers.AdditionalInstanceInitializers.Clear();
199+
if (methodsWithHandles.Any()) _extraUsingDirectives.Add("System.Runtime.CompilerServices");//For MethodImplOptions.Synchronized
204200

205-
if (_methodsWithHandles.Any()) _extraUsingDirectives.Add("System.Runtime.CompilerServices");//For MethodImplOptions.Synchronized
206-
207-
var directlyConvertedMembers = await GetDirectlyConvertMembers();
201+
IEnumerable<MemberDeclarationSyntax> directlyConvertedMembers = await GetDirectlyConvertedMembers(additionalInitializers, methodsWithHandles);
208202

209203
var namedTypeSymbol = _semanticModel.GetDeclaredSymbol(parentType);
210204
bool shouldAddTypeWideInitToThisPart = ShouldAddTypeWideInitToThisPart(parentType, namedTypeSymbol);
@@ -213,19 +207,24 @@ private async Task<IEnumerable<MemberDeclarationSyntax>> ConvertMembers(VBSyntax
213207
if (shouldAddTypeWideInitToThisPart) {
214208
if (requiresInitializeComponent) {
215209
// Constructor event handlers not required since they'll be inside InitializeComponent
216-
directlyConvertedMembers = directlyConvertedMembers.Concat(_methodsWithHandles.CreateDelegatingMethodsRequiredByInitializeComponent());
210+
directlyConvertedMembers = directlyConvertedMembers.Concat(methodsWithHandles.CreateDelegatingMethodsRequiredByInitializeComponent());
217211
} else {
218-
_additionalInitializers.AdditionalInstanceInitializers.AddRange(_methodsWithHandles.GetConstructorEventHandlers());
212+
additionalInitializers.AdditionalInstanceInitializers.AddRange(methodsWithHandles.GetConstructorEventHandlers());
219213
}
220214
}
221215

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

224-
async Task<IEnumerable<MemberDeclarationSyntax>> GetDirectlyConvertMembers()
218+
async Task<MemberDeclarationSyntax[]> GetDirectlyConvertedMembers(AdditionalInitializers additionalInitializers, MethodsWithHandles methodsWithHandles)
225219
{
226-
return await members.SelectManyAsync(async member =>
227-
new[]{await ConvertMember(member)}.Concat(GetAdditionalDeclarations(member)));
228-
220+
_typeContext.Push(methodsWithHandles, additionalInitializers);
221+
try {
222+
var convertedMembers = await members.SelectManyAsync(async member =>
223+
new[] { await ConvertMember(member) }.Concat(GetAdditionalDeclarations(member)));
224+
return convertedMembers.ToArray();
225+
} finally {
226+
_typeContext.Pop();
227+
}
229228
}
230229
}
231230

@@ -515,14 +514,15 @@ private IEnumerable<MemberDeclarationSyntax> CreateWithEventsMembers(SyntaxToken
515514
.Where(a => a.Initializer != null)
516515
.ToDictionary(v => v.Identifier.Text, v => v.Initializer);
517516
var fieldDecl = decl.RemoveNodes(initializers.Values, SyntaxRemoveOptions.KeepNoTrivia);
517+
var initializerState = _typeContext.Initializers;
518518
var initializerCollection = convertedModifiers.Any(m => m.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.StaticKeyword))
519-
? _additionalInitializers.AdditionalStaticInitializers
520-
: _additionalInitializers.AdditionalInstanceInitializers;
519+
? initializerState.AdditionalStaticInitializers
520+
: initializerState.AdditionalInstanceInitializers;
521521
foreach (var initializer in initializers) {
522522
initializerCollection.Add((SyntaxFactory.IdentifierName(initializer.Key), Microsoft.CodeAnalysis.CSharp.SyntaxKind.SimpleAssignmentExpression, initializer.Value.Value));
523523
}
524524

525-
var fieldDecls = _methodsWithHandles.GetDeclarationsForFieldBackedProperty(fieldDecl,
525+
var fieldDecls = _typeContext.MethodsWithHandles.GetDeclarationsForFieldBackedProperty(fieldDecl,
526526
convertedModifiers, SyntaxFactory.List(attributes));
527527
return fieldDecls;
528528
}
@@ -897,7 +897,7 @@ public override async Task<CSharpSyntaxNode> VisitMethodBlock(VBSyntax.MethodBlo
897897

898898
if (node.SubOrFunctionStatement.Identifier.Text == "InitializeComponent" && node.SubOrFunctionStatement.IsKind(VBasic.SyntaxKind.SubStatement) && declaredSymbol.ContainingType.IsDesignerGeneratedTypeWithInitializeComponent(_compilation)) {
899899
var firstResumeLayout = convertedStatements.Statements.FirstOrDefault(IsThisResumeLayoutInvocation) ?? convertedStatements.Statements.Last();
900-
convertedStatements = convertedStatements.InsertNodesBefore(firstResumeLayout, _methodsWithHandles.GetInitializeComponentClassEventHandlers());
900+
convertedStatements = convertedStatements.InsertNodesBefore(firstResumeLayout, _typeContext.MethodsWithHandles.GetInitializeComponentClassEventHandlers());
901901
}
902902

903903
var body = WithImplicitReturnStatements(node, convertedStatements, csReturnVariableOrNull);

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 4 additions & 4 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 MethodsWithHandles _methodsWithHandles;
39+
private readonly TypeContext _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, MethodsWithHandles methodsWithHandles, CommonConversions commonConversions,
48+
Compilation csCompilation, TypeContext typeContext, CommonConversions commonConversions,
4949
HashSet<string> extraUsingDirectives)
5050
{
5151
CommonConversions = commonConversions;
@@ -56,7 +56,7 @@ public ExpressionNodeVisitor(SemanticModel semanticModel,
5656
TriviaConvertingExpressionVisitor = new CommentConvertingVisitorWrapper(this, _semanticModel.SyntaxTree);
5757
_queryConverter = new QueryConverter(commonConversions, TriviaConvertingExpressionVisitor);
5858
_csCompilation = csCompilation;
59-
_methodsWithHandles = methodsWithHandles;
59+
_typeContext = typeContext;
6060
_extraUsingDirectives = extraUsingDirectives;
6161

6262
// If this isn't needed, the assembly with Conversions may not be referenced, so this must be done lazily
@@ -1303,7 +1303,7 @@ public override async Task<CSharpSyntaxNode> VisitTypeArgumentList(VBasic.Syntax
13031303

13041304
public async Task<VBasic.VisualBasicSyntaxVisitor<Task<SyntaxList<StatementSyntax>>>> CreateMethodBodyVisitor(VBasic.VisualBasicSyntaxNode node, bool isIterator = false, IdentifierNameSyntax csReturnVariable = null)
13051305
{
1306-
var methodBodyVisitor = await MethodBodyExecutableStatementVisitor.CreateAsync(node, _semanticModel, TriviaConvertingExpressionVisitor, CommonConversions, _withBlockLhs, _extraUsingDirectives, _additionalLocals, _methodsWithHandles, isIterator, csReturnVariable);
1306+
var methodBodyVisitor = await MethodBodyExecutableStatementVisitor.CreateAsync(node, _semanticModel, TriviaConvertingExpressionVisitor, CommonConversions, _withBlockLhs, _extraUsingDirectives, _additionalLocals, _typeContext, isIterator, csReturnVariable);
13071307
return methodBodyVisitor.CommentConvertingVisitor;
13081308
}
13091309

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace ICSharpCode.CodeConverter.CSharp
2+
{
3+
internal interface ITypeContext
4+
{
5+
AdditionalInitializers Initializers { get; }
6+
MethodsWithHandles MethodsWithHandles { get; }
7+
}
8+
}

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ 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, MethodsWithHandles methodsWithHandles, 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, TypeContext typeContext, bool isIterator, IdentifierNameSyntax csReturnVariable)
4545
{
4646
var solution = commonConversions.Document.Project.Solution;
4747
var declarationsToInlineInLoop = await solution.GetDescendantsToInlineInLoopAsync(semanticModel, node);
48-
return new MethodBodyExecutableStatementVisitor(node, semanticModel, triviaConvertingExpressionVisitor, commonConversions, withBlockLhs, extraUsingDirectives, additionalLocals, methodsWithHandles, declarationsToInlineInLoop) {
48+
return new MethodBodyExecutableStatementVisitor(node, semanticModel, triviaConvertingExpressionVisitor, commonConversions, withBlockLhs, extraUsingDirectives, additionalLocals, typeContext, declarationsToInlineInLoop) {
4949
IsIterator = isIterator,
5050
ReturnVariable = csReturnVariable,
5151
};
@@ -54,15 +54,15 @@ 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, MethodsWithHandles methodsWithHandles, HashSet<ILocalSymbol> localsToInlineInLoop)
57+
HoistedNodeState additionalLocals, TypeContext typeContext, HashSet<ILocalSymbol> localsToInlineInLoop)
5858
{
5959
_methodNode = methodNode;
6060
_semanticModel = semanticModel;
6161
_expressionVisitor = expressionVisitor;
6262
CommonConversions = commonConversions;
6363
_withBlockLhs = withBlockLhs;
6464
_extraUsingDirectives = extraUsingDirectives;
65-
_methodsWithHandles = methodsWithHandles;
65+
_methodsWithHandles = typeContext.MethodsWithHandles;
6666
var byRefParameterVisitor = new HoistedNodeStateVisitor(this, additionalLocals, semanticModel, _generatedNames);
6767
CommentConvertingVisitor = new CommentConvertingMethodBodyVisitor(byRefParameterVisitor);
6868
_vbBooleanTypeSymbol = _semanticModel.Compilation.GetTypeByMetadataName("System.Boolean");

CodeConverter/CSharp/MethodsWithHandles.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,19 @@ internal class MethodsWithHandles
1515
private List<MethodWithHandles> _methodWithHandleses;
1616
private ILookup<string, MethodWithHandles> _handledMethodsFromPropertyWithEventName;
1717

18-
public void Initialize(List<MethodWithHandles> methodWithHandleses)
18+
19+
public MethodsWithHandles(List<MethodWithHandles> methodWithHandleses, ILookup<string, MethodWithHandles> handledMethodsFromPropertyWithEventName)
1920
{
2021
_methodWithHandleses = methodWithHandleses;
21-
_handledMethodsFromPropertyWithEventName = methodWithHandleses
22+
_handledMethodsFromPropertyWithEventName = handledMethodsFromPropertyWithEventName;
23+
}
24+
25+
public static MethodsWithHandles Create(List<MethodWithHandles> methodWithHandleses)
26+
{
27+
var handledMethodsFromPropertyWithEventName = methodWithHandleses
2228
.SelectMany(m => m.HandledPropertyEventCSharpIds.Select(h => (EventPropertyName: h.Item1.Text, MethodWithHandles: m)))
2329
.ToLookup(m => m.EventPropertyName, m => m.MethodWithHandles);
30+
return new MethodsWithHandles(methodWithHandleses, handledMethodsFromPropertyWithEventName);
2431
}
2532

2633
public bool Any()
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.Collections.Generic;
2+
3+
namespace ICSharpCode.CodeConverter.CSharp
4+
{
5+
internal class TypeContext : ITypeContext
6+
{
7+
private readonly Stack<(AdditionalInitializers Initializers, MethodsWithHandles Methods)> _contextStack = new Stack<(AdditionalInitializers Initializers, MethodsWithHandles Methods)>();
8+
9+
public AdditionalInitializers Initializers => _contextStack.Peek().Initializers;
10+
public MethodsWithHandles MethodsWithHandles => _contextStack.Peek().Methods;
11+
12+
public void Push(MethodsWithHandles methodWithHandles, AdditionalInitializers additionalInitializers)
13+
{
14+
_contextStack.Push((additionalInitializers, methodWithHandles));
15+
}
16+
17+
public void Pop() => _contextStack.Pop();
18+
}
19+
}

Tests/CSharp/MemberTests/EventMemberTests.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ End Sub
240240
241241
Sub PrintTestMessage3() Handles NonSharedEventClassInstance.TestEvent
242242
End Sub
243+
244+
Public Class NestedShouldNotGainConstructor
245+
End Class
243246
End Class
244247
245248
Public Class ShouldNotGainConstructor
@@ -339,20 +342,19 @@ public static void PrintTestMessage2()
339342
public void PrintTestMessage3()
340343
{
341344
}
345+
346+
public partial class NestedShouldNotGainConstructor
347+
{
348+
}
342349
}
343350
344351
public partial class ShouldNotGainConstructor
345352
{
346-
static ShouldNotGainConstructor()
347-
{
348-
SharedEventClassInstance = new MyEventClass();
349-
}
350353
}
351354
1 source compilation errors:
352355
BC30516: Overload resolution failed because no accessible 'New' accepts this number of arguments.
353-
2 target compilation errors:
354-
CS1547: Keyword 'void' cannot be used in this context
355-
CS0103: The name 'SharedEventClassInstance' does not exist in the current context", hasLineCommentConversionIssue: true);//TODO: Improve comment mapping for events
356+
1 target compilation errors:
357+
CS1547: Keyword 'void' cannot be used in this context", hasLineCommentConversionIssue: true);//TODO: Improve comment mapping for events
356358
}
357359

358360
[Fact]

0 commit comments

Comments
 (0)