Skip to content

Commit 26bc88e

Browse files
Don't generate unnecessary properties for WithEvents - fixes #572
1 parent 708c82d commit 26bc88e

File tree

12 files changed

+195
-551
lines changed

12 files changed

+195
-551
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1212
* Convert parameterized properties with optional parameters [#597](https://github.com/icsharpcode/CodeConverter/issues/597)
1313
* Convert bitwise negation [#599](https://github.com/icsharpcode/CodeConverter/issues/599)
1414
* No longer adds incorrect "base" qualification for virtual method calls [#600](https://github.com/icsharpcode/CodeConverter/issues/600)
15+
* Don't generate unnecessary properties for WithEvents fields [#572](https://github.com/icsharpcode/CodeConverter/issues/572)
1516

1617
### C# -> VB
1718

CodeConverter/CSharp/CommonConversions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ internal class CommonConversions
4545

4646
private readonly CSharpCompilation _csCompilation;
4747
private readonly ITypeContext _typeContext;
48+
public WinformsConversions WinformsConversions { get; }
4849

4950
public CommentConvertingVisitorWrapper TriviaConvertingExpressionVisitor { get; set; }
5051
public TypeConversionAnalyzer TypeConversionAnalyzer { get; }
@@ -60,6 +61,7 @@ public CommonConversions(Document document, SemanticModel semanticModel,
6061
_csCompilation = csCompilation;
6162
_typeContext = typeContext;
6263
VisualBasicEqualityComparison = visualBasicEqualityComparison;
64+
WinformsConversions = new WinformsConversions(typeContext);
6365
}
6466

6567
public async Task<(IReadOnlyCollection<(VariableDeclarationSyntax Decl, ITypeSymbol Type)> Variables, IReadOnlyCollection<CSharpSyntaxNode> Methods)> SplitVariableDeclarationsAsync(

CodeConverter/CSharp/DeclarationNodeVisitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ private async Task<IEnumerable<MemberDeclarationSyntax>> ConvertMembersAsync(VBS
216216
directlyConvertedMembers = directlyConvertedMembers
217217
.Concat(methodsWithHandles.CreateDelegatingMethodsRequiredByInitializeComponent());
218218
additionalInitializers.AdditionalInstanceInitializers
219-
.AddRange(WinformsConversions.GetNameAssignments(otherPartsOfType));
219+
.AddRange(CommonConversions.WinformsConversions.GetNameAssignments(methodsWithHandles, otherPartsOfType));
220220
} else {
221221
additionalInitializers.AdditionalInstanceInitializers.AddRange(methodsWithHandles.GetConstructorEventHandlers());
222222
}

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public override async Task<CSharpSyntaxNode> VisitLiteralExpression(VBasic.Synta
269269

270270
var val = node.Token.Value;
271271
var text = node.Token.Text;
272-
if (WinformsConversions.ShouldPrefixAssignedNameWithUnderscore(node.Parent as VBSyntax.AssignmentStatementSyntax) && val is string valStr) {
272+
if (_typeContext.Any() && CommonConversions.WinformsConversions.ShouldPrefixAssignedNameWithUnderscore(node.Parent as VBSyntax.AssignmentStatementSyntax, _typeContext.MethodsWithHandles) && val is string valStr) {
273273
val = "_" + valStr;
274274
text = "\"_" + valStr + "\"";
275275
}

CodeConverter/CSharp/ITypeContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ internal interface ITypeContext
55
AdditionalInitializers Initializers { get; }
66
MethodsWithHandles MethodsWithHandles { get; }
77
HoistedNodeState HoistedState { get; }
8+
bool Any();
89
}
910
}

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,19 @@ private async Task<SyntaxList<StatementSyntax>> ConvertMidAssignmentAsync(VBSynt
197197
private SyntaxList<StatementSyntax> GetPostAssignmentStatements(VBSyntax.AssignmentStatementSyntax node)
198198
{
199199
var potentialPropertySymbol = _semanticModel.GetSymbolInfo(node.Left).ExtractBestMatch<ISymbol>();
200-
return _methodsWithHandles.GetPostAssignmentStatements(node, potentialPropertySymbol);
200+
return GetPostAssignmentStatements(node, potentialPropertySymbol);
201+
}
202+
203+
/// <summary>
204+
/// Make winforms designer work: https://github.com/icsharpcode/CodeConverter/issues/321
205+
/// </summary>
206+
public SyntaxList<StatementSyntax> GetPostAssignmentStatements(Microsoft.CodeAnalysis.VisualBasic.Syntax.AssignmentStatementSyntax node, ISymbol potentialPropertySymbol)
207+
{
208+
if (CommonConversions.WinformsConversions.MustInlinePropertyWithEventsAccess(node, potentialPropertySymbol)) {
209+
return _methodsWithHandles.GetPostAssignmentStatements(potentialPropertySymbol);
210+
}
211+
212+
return SyntaxFactory.List<StatementSyntax>();
201213
}
202214

203215
public override async Task<SyntaxList<StatementSyntax>> VisitEraseStatement(VBSyntax.EraseStatementSyntax node)

CodeConverter/CSharp/MethodsWithHandles.cs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Collections.Immutable;
34
using System.Linq;
45
using ICSharpCode.CodeConverter.Util;
56
using Microsoft.CodeAnalysis;
@@ -15,47 +16,47 @@ internal class MethodsWithHandles
1516
{
1617
private readonly List<MethodWithHandles> _methodWithHandleses;
1718
private readonly ILookup<string, MethodWithHandles> _handledMethodsFromPropertyWithEventName;
19+
private readonly ImmutableHashSet<string> _containerFieldsConvertedToProperties;
1820

1921

20-
public MethodsWithHandles(List<MethodWithHandles> methodWithHandleses, ILookup<string, MethodWithHandles> handledMethodsFromPropertyWithEventName)
22+
private MethodsWithHandles(List<MethodWithHandles> methodWithHandleses,
23+
ILookup<string, MethodWithHandles> handledMethodsFromPropertyWithEventName,
24+
ImmutableHashSet<string> containerFieldsConvertedToProperties)
2125
{
2226
_methodWithHandleses = methodWithHandleses;
2327
_handledMethodsFromPropertyWithEventName = handledMethodsFromPropertyWithEventName;
28+
_containerFieldsConvertedToProperties = containerFieldsConvertedToProperties;
2429
}
2530

2631
public static MethodsWithHandles Create(List<MethodWithHandles> methodWithHandleses)
2732
{
2833
var handledMethodsFromPropertyWithEventName = methodWithHandleses
29-
.SelectMany(m => m.HandledPropertyEventCSharpIds.Select(h => (EventPropertyName: h.Item1.Text, MethodWithHandles: m)))
34+
.SelectMany(m => m.HandledPropertyEventCSharpIds.Select(h => (EventPropertyName: h.EventContainerName.Text, MethodWithHandles: m)))
3035
.ToLookup(m => m.EventPropertyName, m => m.MethodWithHandles);
31-
return new MethodsWithHandles(methodWithHandleses, handledMethodsFromPropertyWithEventName);
36+
var containerFieldsConvertedToProperties = methodWithHandleses
37+
.SelectMany(m => m.HandledPropertyEventCSharpIds, (_, handled) => handled.EventContainerName.Text)
38+
.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase);
39+
return new MethodsWithHandles(methodWithHandleses, handledMethodsFromPropertyWithEventName, containerFieldsConvertedToProperties);
3240
}
3341

34-
public bool Any()
35-
{
36-
return _methodWithHandleses.Any();
37-
}
42+
public bool Any() => _methodWithHandleses.Any();
43+
public bool AnyForPropertyName(string propertyIdentifierText) => _containerFieldsConvertedToProperties.Contains(propertyIdentifierText);
3844

3945
public IEnumerable<MemberDeclarationSyntax> GetDeclarationsForFieldBackedProperty(VariableDeclarationSyntax fieldDecl, SyntaxTokenList convertedModifiers, SyntaxList<AttributeListSyntax> attributes)
4046
{
4147
return MethodWithHandles.GetDeclarationsForFieldBackedProperty(fieldDecl, convertedModifiers, attributes,
4248
_methodWithHandleses);
4349
}
4450

45-
46-
/// <summary>
47-
/// Make winforms designer work: https://github.com/icsharpcode/CodeConverter/issues/321
48-
/// </summary>
49-
public SyntaxList<StatementSyntax> GetPostAssignmentStatements(Microsoft.CodeAnalysis.VisualBasic.Syntax.AssignmentStatementSyntax node, ISymbol potentialPropertySymbol)
51+
public SyntaxList<StatementSyntax> GetPostAssignmentStatements(ISymbol potentialPropertySymbol)
5052
{
51-
if (WinformsConversions.MustInlinePropertyWithEventsAccess(node, potentialPropertySymbol))
53+
var fieldName = SyntaxFactory.IdentifierName("_" + potentialPropertySymbol.Name);
54+
var handledMethods = _handledMethodsFromPropertyWithEventName[potentialPropertySymbol.Name].ToArray();
55+
if (handledMethods.Any())
5256
{
53-
var fieldName = SyntaxFactory.IdentifierName("_" + potentialPropertySymbol.Name);
54-
var handledMethods = _handledMethodsFromPropertyWithEventName[potentialPropertySymbol.Name].ToArray();
55-
if (handledMethods.Any())
57+
var postAssignmentStatements = handledMethods.SelectMany(h =>
58+
h.GetPostInitializationStatements(potentialPropertySymbol.Name, fieldName));
5659
{
57-
var postAssignmentStatements = handledMethods.SelectMany(h =>
58-
h.GetPostInitializationStatements(potentialPropertySymbol.Name, fieldName));
5960
return SyntaxFactory.List(postAssignmentStatements);
6061
}
6162
}

CodeConverter/CSharp/TypeContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ public void Push(MethodsWithHandles methodWithHandles, AdditionalInitializers ad
1717
}
1818

1919
public void Pop() => _contextStack.Pop();
20+
public bool Any() => _contextStack.Count > 0;
2021
}
2122
}

CodeConverter/CSharp/WinformsConversions.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,22 @@
99

1010
namespace ICSharpCode.CodeConverter.CSharp
1111
{
12-
internal static class WinformsConversions
12+
internal class WinformsConversions
1313
{
14+
private readonly ITypeContext _typeContext;
15+
16+
public WinformsConversions(ITypeContext typeContext)
17+
{
18+
_typeContext = typeContext;
19+
}
20+
1421
/// <remarks>
1522
/// Co-ordinates inlining property events, see <see cref="MethodBodyExecutableStatementVisitor.GetPostAssignmentStatements"/>
1623
/// Also see usages of IsDesignerGeneratedTypeWithInitializeComponent
1724
/// </remarks>
18-
public static bool MustInlinePropertyWithEventsAccess(SyntaxNode anyNodePossiblyWithinMethod, ISymbol potentialPropertySymbol)
25+
public bool MustInlinePropertyWithEventsAccess(SyntaxNode anyNodePossiblyWithinMethod, ISymbol potentialPropertySymbol)
1926
{
20-
return InMethodCalledInitializeComponent(anyNodePossiblyWithinMethod) && potentialPropertySymbol is IPropertySymbol prop && prop.IsWithEvents;
27+
return potentialPropertySymbol != null &&_typeContext.Any() && _typeContext.MethodsWithHandles.AnyForPropertyName(potentialPropertySymbol.Name) && InMethodCalledInitializeComponent(anyNodePossiblyWithinMethod) && potentialPropertySymbol is IPropertySymbol prop && prop.IsWithEvents;
2128
}
2229

2330
public static bool InMethodCalledInitializeComponent(SyntaxNode anyNodePossiblyWithinMethod)
@@ -34,33 +41,32 @@ private static bool IsInitializeComponent(VBSyntax.MethodBlockSyntax methodBlock
3441
/// <summary>
3542
/// We replace a field with a property to handle event subscription, so need to update the name so the winforms designer regenerates the file correctly in future
3643
/// </summary>
37-
/// <returns></returns>
38-
public static bool ShouldPrefixAssignedNameWithUnderscore(VBSyntax.StatementSyntax statementOrNull)
44+
public bool ShouldPrefixAssignedNameWithUnderscore(VBSyntax.StatementSyntax statementOrNull, MethodsWithHandles typeContextMethodsWithHandles)
3945
{
4046
return statementOrNull is VBSyntax.AssignmentStatementSyntax assignment && InMethodCalledInitializeComponent(assignment) &&
4147
assignment.Left is VBSyntax.MemberAccessExpressionSyntax maes &&
4248
!(maes.Expression is VBSyntax.MeExpressionSyntax) &&
43-
maes.Name.ToString() == "Name";
44-
}
45-
46-
private static T LastOrDefaultDescendant<T>(this VBasic.VisualBasicSyntaxNode syntaxNode) {
47-
return syntaxNode.DescendantNodes().OfType<T>().LastOrDefault();
49+
maes.Name.ToString() == "Name" &&
50+
maes.Expression.LastOrDefaultDescendant<VBSyntax.IdentifierNameSyntax>()?.Identifier.Text is {} propName &&
51+
typeContextMethodsWithHandles.AnyForPropertyName(propName);
4852
}
4953

50-
internal static IEnumerable<Assignment> GetNameAssignments((VBSyntax.TypeBlockSyntax Type, SemanticModel SemanticModel)[] otherPartsOfType)
54+
public IEnumerable<Assignment> GetNameAssignments(MethodsWithHandles typeContextMethodsWithHandles,
55+
(VBSyntax.TypeBlockSyntax Type, SemanticModel SemanticModel)[] otherPartsOfType)
5156
{
5257
return otherPartsOfType.SelectMany(typePart =>
5358
typePart.Type.Members.OfType<VBSyntax.MethodBlockSyntax>()
5459
.Where(IsInitializeComponent)
55-
.SelectMany(GetAssignments)
60+
.SelectMany(syntax => GetAssignments(typeContextMethodsWithHandles, syntax))
5661
);
5762
}
5863

59-
private static IEnumerable<Assignment> GetAssignments(VBSyntax.MethodBlockSyntax initializeComponent)
64+
private IEnumerable<Assignment> GetAssignments(MethodsWithHandles typeContextMethodsWithHandles,
65+
VBSyntax.MethodBlockSyntax initializeComponent)
6066
{
6167
return initializeComponent.Statements
6268
.OfType<VBSyntax.AssignmentStatementSyntax>()
63-
.Where(ShouldPrefixAssignedNameWithUnderscore)
69+
.Where(syntax => ShouldPrefixAssignedNameWithUnderscore(syntax, typeContextMethodsWithHandles))
6470
.Select(s => (s.Left as VBSyntax.MemberAccessExpressionSyntax)?.Expression.LastOrDefaultDescendant<VBSyntax.IdentifierNameSyntax>())
6571
.Where(s => s != null)
6672
.Select(id => {

CodeConverter/Util/SyntaxNodeExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,5 +516,9 @@ public static SyntaxToken FindNonZeroWidthToken(this SyntaxNode node, int positi
516516
return syntaxToken;
517517
}
518518
}
519+
520+
public static T LastOrDefaultDescendant<T>(this SyntaxNode syntaxNode) {
521+
return syntaxNode.DescendantNodes().OfType<T>().LastOrDefault();
522+
}
519523
}
520524
}

0 commit comments

Comments
 (0)