Skip to content

Commit 2345ca1

Browse files
Deal with more combinations of explicit / parameters / myclass
1 parent 65e5a98 commit 2345ca1

File tree

3 files changed

+123
-46
lines changed

3 files changed

+123
-46
lines changed

CodeConverter/CSharp/DeclarationNodeVisitor.cs

Lines changed: 97 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -679,15 +679,20 @@ string GetCSharpIdentifierText(VBSyntax.EventContainerSyntax p)
679679

680680
public override async Task<CSharpSyntaxNode> VisitPropertyStatement(VBSyntax.PropertyStatementSyntax node)
681681
{
682-
var attributes = await node.AttributeLists.SelectManyAsync(CommonConversions.ConvertAttributeAsync);
682+
var attributes = SyntaxFactory.List(await node.AttributeLists.SelectManyAsync(CommonConversions.ConvertAttributeAsync));
683683
var isReadonly = node.Modifiers.Any(m => SyntaxTokenExtensions.IsKind(m, VBasic.SyntaxKind.ReadOnlyKeyword));
684684
var isWriteOnly = node.Modifiers.Any(m => SyntaxTokenExtensions.IsKind(m, VBasic.SyntaxKind.WriteOnlyKeyword));
685685
var convertibleModifiers = node.Modifiers.Where(m => !m.IsKind(VBasic.SyntaxKind.ReadOnlyKeyword, VBasic.SyntaxKind.WriteOnlyKeyword, VBasic.SyntaxKind.DefaultKeyword));
686686
var modifiers = CommonConversions.ConvertModifiers(node, convertibleModifiers.ToList(), GetMemberContext(node));
687687
var isIndexer = CommonConversions.IsDefaultIndexer(node);
688-
var nodeSymbol = ModelExtensions.GetDeclaredSymbol(_semanticModel, node);
689-
var accessedThroughMyClass = IsAccessedThroughMyClass(node, node.Identifier, nodeSymbol);
688+
var propSymbol = ModelExtensions.GetDeclaredSymbol(_semanticModel, node) as IPropertySymbol;
689+
var accessedThroughMyClass = IsAccessedThroughMyClass(node, node.Identifier, propSymbol);
690690
bool hasImplementation = !node.Modifiers.Any(m => m.IsKind(VBasic.SyntaxKind.MustOverrideKeyword)) && node.GetAncestor<VBSyntax.InterfaceBlockSyntax>() == null;
691+
var explicitInterfaceSpecifier = propSymbol.DeclaredAccessibility == Accessibility.Private && propSymbol.ExplicitInterfaceImplementations.Any() ?
692+
SyntaxFactory.ExplicitInterfaceSpecifier(SyntaxFactory.IdentifierName(propSymbol.ExplicitInterfaceImplementations.First().ContainingType.Name))
693+
: null;
694+
var shouldConvertToMethods = await ShouldConvertAsParameterizedPropertyAsync(node);
695+
691696

692697
var initializer = (EqualsValueClauseSyntax) await node.Initializer.AcceptAsync(_triviaConvertingExpressionVisitor);
693698
VBSyntax.TypeSyntax vbType;
@@ -711,14 +716,13 @@ public override async Task<CSharpSyntaxNode> VisitPropertyStatement(VBSyntax.Pro
711716

712717
AccessorListSyntax accessors = null;
713718
if (node.Parent is VBSyntax.PropertyBlockSyntax propertyBlock) {
714-
if (node.ParameterList?.Parameters.Any() == true && !isIndexer) {
719+
if (shouldConvertToMethods) {
715720
if (accessedThroughMyClass) {
716721
// Would need to create a delegating implementation to implement this
717722
throw new NotImplementedException("MyClass indexing not implemented");
718723
}
719-
720724
var methodDeclarationSyntaxs = await propertyBlock.Accessors.SelectAsync(async a =>
721-
(MethodDeclarationSyntax) await a.AcceptAsync(TriviaConvertingDeclarationVisitor));
725+
(MethodDeclarationSyntax)await a.AcceptAsync(TriviaConvertingDeclarationVisitor, a == propertyBlock.Accessors.First() ? SourceTriviaMapKind.All : SourceTriviaMapKind.None));
722726
var accessorMethods = methodDeclarationSyntaxs.Select(WithMergedModifiers).ToArray();
723727
_additionalDeclarations.Add(propertyBlock, accessorMethods.Skip(1).ToArray());
724728
return accessorMethods[0];
@@ -727,11 +731,24 @@ public override async Task<CSharpSyntaxNode> VisitPropertyStatement(VBSyntax.Pro
727731
accessors = SyntaxFactory.AccessorList(
728732
SyntaxFactory.List(
729733
(await propertyBlock.Accessors.SelectAsync(async a =>
730-
(AccessorDeclarationSyntax) await a.AcceptAsync(TriviaConvertingDeclarationVisitor))
734+
(AccessorDeclarationSyntax)await a.AcceptAsync(TriviaConvertingDeclarationVisitor))
731735
)
732736
));
737+
} else if (shouldConvertToMethods && propSymbol.ContainingType.IsInterfaceType()) {
738+
var methodDeclarationSyntaxs = new List<MemberDeclarationSyntax>();
739+
if (propSymbol.GetMethod != null) {
740+
methodDeclarationSyntaxs.Add(await CreateMethodDeclarationSyntaxAsync(node.ParameterList, GetMethodId(node), false));
741+
}
742+
if (propSymbol.SetMethod != null) {
743+
var setMethod = await CreateMethodDeclarationSyntaxAsync(node.ParameterList, SetMethodId(node), true);
744+
var valueParam = SyntaxFactory.Parameter(CommonConversions.CsEscapedIdentifier("value")).WithType(rawType);
745+
setMethod = setMethod.AddParameterListParameters(valueParam);
746+
methodDeclarationSyntaxs.Add(setMethod);
747+
}
748+
_additionalDeclarations.Add(node, methodDeclarationSyntaxs.Skip(1).ToArray());
749+
return methodDeclarationSyntaxs[0];
733750
} else {
734-
accessors = ConvertSimpleAccessors(isWriteOnly, isReadonly, hasImplementation, nodeSymbol.DeclaredAccessibility);
751+
accessors = ConvertSimpleAccessors(isWriteOnly, isReadonly, hasImplementation, propSymbol.DeclaredAccessibility);
735752
}
736753

737754

@@ -755,17 +772,21 @@ public override async Task<CSharpSyntaxNode> VisitPropertyStatement(VBSyntax.Pro
755772
var csIdentifier = CommonConversions.ConvertIdentifier(node.Identifier);
756773

757774
if (accessedThroughMyClass) {
758-
string csIndentifierName = AddRealPropertyDelegatingToMyClassVersion(node, csIdentifier, attributes, modifiers, rawType);
775+
776+
var realModifiers = modifiers.RemoveOnly(m => m.IsKind(CSSyntaxKind.PrivateKeyword));
777+
string csIndentifierName = AddRealPropertyDelegatingToMyClassVersion(node, csIdentifier, attributes, realModifiers, rawType);
759778
modifiers = modifiers.Remove(modifiers.Single(m => m.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.VirtualKeyword)));
760779
csIdentifier = SyntaxFactory.Identifier(csIndentifierName);
780+
} else if (explicitInterfaceSpecifier != null) {
781+
modifiers = modifiers.RemoveOnly(m => m.IsKind(CSSyntaxKind.PrivateKeyword));
761782
}
762783

763784
var semicolonToken = SyntaxFactory.Token(initializer == null ? Microsoft.CodeAnalysis.CSharp.SyntaxKind.None : Microsoft.CodeAnalysis.CSharp.SyntaxKind.SemicolonToken);
764785
return SyntaxFactory.PropertyDeclaration(
765-
SyntaxFactory.List(attributes),
786+
attributes,
766787
modifiers,
767788
rawType,
768-
null,
789+
explicitInterfaceSpecifier,
769790
csIdentifier, accessors,
770791
null,
771792
initializer,
@@ -777,24 +798,39 @@ MemberDeclarationSyntax WithMergedModifiers(MethodDeclarationSyntax member)
777798
SyntaxTokenList originalModifiers = member.GetModifiers();
778799
var hasVisibility = originalModifiers.Any(m => m.IsCsVisibility(false, false));
779800
var modifiersToAdd = hasVisibility ? modifiers.Where(m => !m.IsCsVisibility(false, false)) : modifiers;
780-
return member.WithModifiers(SyntaxFactory.TokenList(originalModifiers.Concat(modifiersToAdd)));
801+
var newModifiers = SyntaxFactory.TokenList(originalModifiers.Concat(modifiersToAdd));
802+
if (explicitInterfaceSpecifier != null) newModifiers = SyntaxFactory.TokenList(newModifiers.Where(m => !m.IsCsVisibility(false, false)));
803+
return member.WithModifiers(newModifiers);
804+
}
805+
806+
async Task<MethodDeclarationSyntax> CreateMethodDeclarationSyntaxAsync(VBSyntax.ParameterListSyntax containingPropParameterList, string methodId, bool voidReturn)
807+
{
808+
var parameterListSyntax = (ParameterListSyntax) await containingPropParameterList.AcceptAsync(_triviaConvertingExpressionVisitor);
809+
var methodModifiers = SyntaxFactory.TokenList(modifiers.Where(m => !m.IsCsVisibility(false, false)));
810+
MethodDeclarationSyntax methodDeclarationSyntax = SyntaxFactory.MethodDeclaration(attributes, methodModifiers,
811+
voidReturn ? SyntaxFactory.PredefinedType(SyntaxFactory.Token(CSSyntaxKind.VoidKeyword)) : rawType,
812+
explicitInterfaceSpecifier,
813+
SyntaxFactory.Identifier(methodId), null,
814+
(ParameterListSyntax)parameterListSyntax, SyntaxFactory.List<TypeParameterConstraintClauseSyntax>(), null, null)
815+
.WithSemicolonToken(SyntaxFactory.Token(CSSyntaxKind.SemicolonToken));
816+
return methodDeclarationSyntax;
781817
}
782818
}
783819

784820
private string AddRealPropertyDelegatingToMyClassVersion(VBSyntax.PropertyStatementSyntax node, SyntaxToken csIdentifier,
785-
AttributeListSyntax[] attributes, SyntaxTokenList modifiers, TypeSyntax rawType)
821+
SyntaxList<AttributeListSyntax> attributes, SyntaxTokenList modifiers, TypeSyntax rawType)
786822
{
787823
var csIndentifierName = "MyClass" + csIdentifier.ValueText;
788824
ExpressionSyntax thisDotIdentifier = SyntaxFactory.ParseExpression($"this.{csIndentifierName}");
789825
var getReturn = SyntaxFactory.Block(SyntaxFactory.ReturnStatement(thisDotIdentifier));
790826
var getAccessor = SyntaxFactory.AccessorDeclaration(Microsoft.CodeAnalysis.CSharp.SyntaxKind.GetAccessorDeclaration, getReturn);
791827
var setValue = SyntaxFactory.Block(SyntaxFactory.ExpressionStatement(
792828
SyntaxFactory.AssignmentExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SimpleAssignmentExpression, thisDotIdentifier,
793-
SyntaxFactory.IdentifierName("value"))));
829+
SyntaxFactory.IdentifierName(CommonConversions.CsEscapedIdentifier("value")))));
794830
var setAccessor = SyntaxFactory.AccessorDeclaration(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SetAccessorDeclaration, setValue);
795831
var realAccessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {getAccessor, setAccessor}));
796832
var realDecl = SyntaxFactory.PropertyDeclaration(
797-
SyntaxFactory.List(attributes),
833+
attributes,
798834
modifiers,
799835
rawType,
800836
null,
@@ -851,34 +887,40 @@ public override async Task<CSharpSyntaxNode> VisitAccessorBlock(VBSyntax.Accesso
851887
{
852888
Microsoft.CodeAnalysis.CSharp.SyntaxKind blockKind;
853889
bool isIterator = node.IsIterator();
890+
var ancestoryPropertyBlock = node.GetAncestor<VBSyntax.PropertyBlockSyntax>();
891+
var containingPropertyStmt = ancestoryPropertyBlock?.PropertyStatement;
854892
var csReturnVariableOrNull = CommonConversions.GetRetVariableNameOrNull(node);
855893
var convertedStatements = await ConvertStatementsAsync(node.Statements, await CreateMethodBodyVisitorAsync(node, isIterator));
856894
var body = WithImplicitReturnStatements(node, convertedStatements, csReturnVariableOrNull);
857895
var attributes = await CommonConversions.ConvertAttributesAsync(node.AccessorStatement.AttributeLists);
858896
var modifiers = CommonConversions.ConvertModifiers(node, node.AccessorStatement.Modifiers, TokenContext.Local);
859897
string potentialMethodId;
860-
var ancestoryPropertyBlock = node.GetAncestor<VBSyntax.PropertyBlockSyntax>();
861-
var containingPropertyStmt = ancestoryPropertyBlock?.PropertyStatement;
898+
var declaredParentSymbol = containingPropertyStmt != null ? _semanticModel.GetDeclaredSymbol(containingPropertyStmt) : null;
899+
var explicitInterfaceSpecifier = declaredParentSymbol is IPropertySymbol propSymbol && propSymbol.DeclaredAccessibility == Accessibility.Private && propSymbol.ExplicitInterfaceImplementations.Any() ?
900+
SyntaxFactory.ExplicitInterfaceSpecifier(SyntaxFactory.IdentifierName(propSymbol.ExplicitInterfaceImplementations.First().ContainingType.Name))
901+
: null;
862902
var sourceMap = ancestoryPropertyBlock?.Accessors.FirstOrDefault() == node ? SourceTriviaMapKind.All : SourceTriviaMapKind.None;
903+
var returnType = containingPropertyStmt?.AsClause is VBSyntax.SimpleAsClauseSyntax asClause ?
904+
await asClause.Type.AcceptAsync<TypeSyntax>(_triviaConvertingExpressionVisitor, sourceMap) :
905+
SyntaxFactory.PredefinedType(SyntaxFactory.Token(CSSyntaxKind.VoidKeyword));
906+
863907
switch (node.Kind()) {
864908
case VBasic.SyntaxKind.GetAccessorBlock:
865909
blockKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind.GetAccessorDeclaration;
866-
potentialMethodId = $"get_{(containingPropertyStmt.Identifier.Text)}";
910+
potentialMethodId = GetMethodId(containingPropertyStmt);
867911

868-
if (containingPropertyStmt.AsClause is VBSyntax.SimpleAsClauseSyntax getAsClause &&
869-
await ShouldConvertAsParameterizedProperty()) {
870-
var method = await CreateMethodDeclarationSyntax(containingPropertyStmt?.ParameterList);
871-
return method.WithReturnType((TypeSyntax) await getAsClause.Type.AcceptAsync(_triviaConvertingExpressionVisitor, sourceMap));
912+
if (await ShouldConvertAsParameterizedPropertyAsync(containingPropertyStmt)) {
913+
var method = await CreateMethodDeclarationSyntax(containingPropertyStmt?.ParameterList, false);
914+
return method;
872915
}
873916
break;
874917
case VBasic.SyntaxKind.SetAccessorBlock:
875918
blockKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind.SetAccessorDeclaration;
876-
potentialMethodId = $"set_{(containingPropertyStmt.Identifier.Text)}";
919+
potentialMethodId = SetMethodId(containingPropertyStmt);
877920

878-
if (containingPropertyStmt.AsClause is VBSyntax.SimpleAsClauseSyntax setAsClause && await ShouldConvertAsParameterizedProperty()) {
879-
var setMethod = await CreateMethodDeclarationSyntax(containingPropertyStmt?.ParameterList);
880-
var valueParameterType = (TypeSyntax) await setAsClause.Type.AcceptAsync(_triviaConvertingExpressionVisitor, sourceMap);
881-
return setMethod.AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("value")).WithType(valueParameterType));
921+
if (await ShouldConvertAsParameterizedPropertyAsync(containingPropertyStmt)) {
922+
var setMethod = await CreateMethodDeclarationSyntax(containingPropertyStmt?.ParameterList, true);
923+
return setMethod.AddParameterListParameters(SyntaxFactory.Parameter(CommonConversions.CsEscapedIdentifier("value")).WithType(returnType));
882924
}
883925
break;
884926
case VBasic.SyntaxKind.AddHandlerAccessorBlock:
@@ -891,33 +933,46 @@ await ShouldConvertAsParameterizedProperty()) {
891933
var eventStatement = ((VBSyntax.EventBlockSyntax)node.Parent).EventStatement;
892934
var eventName = CommonConversions.ConvertIdentifier(eventStatement.Identifier).ValueText;
893935
potentialMethodId = $"On{eventName}";
894-
return await CreateMethodDeclarationSyntax(node.AccessorStatement.ParameterList);
936+
return await CreateMethodDeclarationSyntax(node.AccessorStatement.ParameterList, true);
895937
default:
896938
throw new NotSupportedException(node.Kind().ToString());
897939
}
898940

899941
return SyntaxFactory.AccessorDeclaration(blockKind, attributes, modifiers, body);
900942

901-
async Task<bool> ShouldConvertAsParameterizedProperty()
902-
{
903-
if (containingPropertyStmt.ParameterList?.Parameters.Any() == true && !CommonConversions.IsDefaultIndexer(containingPropertyStmt)) {
904-
return true;
905-
}
906-
907-
return false;
908-
}
909-
910-
async Task<MethodDeclarationSyntax> CreateMethodDeclarationSyntax(VBSyntax.ParameterListSyntax containingPropParameterList)
943+
async Task<MethodDeclarationSyntax> CreateMethodDeclarationSyntax(VBSyntax.ParameterListSyntax containingPropParameterList, bool voidReturn)
911944
{
912945
var parameterListSyntax = await containingPropParameterList.AcceptAsync(_triviaConvertingExpressionVisitor, sourceMap);
913-
MethodDeclarationSyntax methodDeclarationSyntax = SyntaxFactory.MethodDeclaration(attributes, modifiers,
914-
SyntaxFactory.PredefinedType(SyntaxFactory.Token(Microsoft.CodeAnalysis.CSharp.SyntaxKind.VoidKeyword)), null,
946+
var methodModifiers = explicitInterfaceSpecifier != null ? modifiers.RemoveOnly(x => x.IsKind(CSSyntaxKind.PrivateKeyword))
947+
: modifiers;
948+
MethodDeclarationSyntax methodDeclarationSyntax = SyntaxFactory.MethodDeclaration(attributes, methodModifiers,
949+
voidReturn ? SyntaxFactory.PredefinedType(SyntaxFactory.Token(CSSyntaxKind.VoidKeyword)) : returnType,
950+
explicitInterfaceSpecifier,
915951
SyntaxFactory.Identifier(potentialMethodId), null,
916-
(ParameterListSyntax) parameterListSyntax, SyntaxFactory.List<TypeParameterConstraintClauseSyntax>(), body, null);
952+
(ParameterListSyntax)parameterListSyntax, SyntaxFactory.List<TypeParameterConstraintClauseSyntax>(), body, null);
917953
return methodDeclarationSyntax;
918954
}
919955
}
920956

957+
private static string SetMethodId(VBSyntax.PropertyStatementSyntax containingPropertyStmt)
958+
{
959+
return $"set_{(containingPropertyStmt.Identifier.Text)}";
960+
}
961+
962+
private static string GetMethodId(VBSyntax.PropertyStatementSyntax containingPropertyStmt)
963+
{
964+
return $"get_{(containingPropertyStmt.Identifier.Text)}";
965+
}
966+
967+
private async Task<bool> ShouldConvertAsParameterizedPropertyAsync(VBSyntax.PropertyStatementSyntax propStmt)
968+
{
969+
if (propStmt.ParameterList?.Parameters.Any() == true && !CommonConversions.IsDefaultIndexer(propStmt)) {
970+
return true;
971+
}
972+
973+
return false;
974+
}
975+
921976
public override async Task<CSharpSyntaxNode> VisitAccessorStatement(VBSyntax.AccessorStatementSyntax node)
922977
{
923978
return SyntaxFactory.AccessorDeclaration(node.Kind().ConvertToken(), null);
@@ -1079,7 +1134,7 @@ public override async Task<CSharpSyntaxNode> VisitMethodStatement(VBSyntax.Metho
10791134
null,
10801135
arrowClause,
10811136
SyntaxFactory.Token(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SemicolonToken)
1082-
));
1137+
).WithoutSourceMapping());
10831138
}
10841139
// If the method is virtual, and there is a MyClass.SomeMethod() call,
10851140
// we need to emit a non-virtual method for it to call

CodeConverter/Shared/AsyncEnumerableTaskExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Linq;
45
using System.Runtime.CompilerServices;
56
using System.Threading;
@@ -8,6 +9,7 @@
89

910
namespace ICSharpCode.CodeConverter.Shared
1011
{
12+
[DebuggerStepThrough]
1113
public static class AsyncEnumerableTaskExtensions
1214
{
1315
public static async Task<TResult[]> SelectManyAsync<TArg, TResult>(this IEnumerable<TArg> nodes,

0 commit comments

Comments
 (0)