From e9cf4f21127265e86c6979696665f0e0c9b68564 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Wed, 31 Jul 2019 22:23:35 +0100 Subject: [PATCH 01/12] Failing test for parameterized property --- Tests/CSharp/MemberTests.cs | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Tests/CSharp/MemberTests.cs b/Tests/CSharp/MemberTests.cs index 58e541f0a..5a9034a6e 100644 --- a/Tests/CSharp/MemberTests.cs +++ b/Tests/CSharp/MemberTests.cs @@ -545,6 +545,47 @@ public int Test3 }"); } + [Fact] + public void TestParameterizedProperty() + { + TestConversionVisualBasicToCSharpWithoutComments( +@"Class TestClass + Public Property FirstName As String + Public Property LastName As String + + Public ReadOnly Property FullName(ByVal lastNameFirst As Boolean) As String + Get + If lastNameFirst Then + Return LastName & "" "" & FirstName + Else + Return FirstName & "" "" & LastName + End If + End Get + End Property + + Public Overrides Function ToString() As String + Return FullName(False) + End Function +End Class", @"class TestClass +{ + public string FirstName { get; set; } + public string LastName { get; set; } + + public string get_FullName(bool lastNameFirst) + { + if (lastNameFirst) + return LastName + "" "" + FirstName; + else + return FirstName + "" "" + LastName; + } + + public override string ToString() + { + return get_FullName(false); + } +}"); + } + [Fact] public void TestReadWriteOnlyInterfaceProperty() { From 82a97cb9589d37990da0c8e28357fa1bc1779b58 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Wed, 31 Jul 2019 22:39:40 +0100 Subject: [PATCH 02/12] Extract methods --- .../CSharp/NodesVisitor.cs | 120 +++++++++++------- 1 file changed, 76 insertions(+), 44 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index d6e5b2d59..e5051a37e 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -568,32 +568,7 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme _ => { throw new NotImplementedException($"{_.GetType().FullName} not implemented!"); } )?.Accept(TriviaConvertingVisitor) ?? VarType; - AccessorListSyntax accessors = null; - if (!hasBody) { - var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SemicolonToken); - var setAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SemicolonToken); - if (isWriteOnly) { - getAccessor = getAccessor.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); - } - if (isReadonly) { - setAccessor = setAccessor.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); - } - if (isInInterface && isReadonly) { - accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] { getAccessor })); - } else if (isInInterface && isWriteOnly) { - accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] { setAccessor })); - } else { - // In VB, there's a backing field which can always be read and written to even on ReadOnly/WriteOnly properties. - // Our conversion will rewrite usages of that field to use the property accessors which therefore must exist and be private at minimum. - accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] { getAccessor, setAccessor })); - } - } else { - accessors = SyntaxFactory.AccessorList( - SyntaxFactory.List( - ((VBSyntax.PropertyBlockSyntax)node.Parent).Accessors.Select(a => (AccessorDeclarationSyntax)a.Accept(TriviaConvertingVisitor)) - ) - ); - } + AccessorListSyntax accessors = GetPropertyAccesors(node, hasBody, isWriteOnly, isReadonly, isInInterface); if (isIndexer) { if (accessedThroughMyClass) { @@ -612,24 +587,7 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme } else { var csIdentifier = ConvertIdentifier(node.Identifier); if (accessedThroughMyClass) { - var csIndentifierName = "MyClass" + csIdentifier.ValueText; - ExpressionSyntax thisDotIdentifier = SyntaxFactory.ParseExpression($"this.{csIndentifierName}"); - var getReturn = SyntaxFactory.Block(SyntaxFactory.ReturnStatement(thisDotIdentifier)); - var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, getReturn); - var setValue = SyntaxFactory.Block(SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, thisDotIdentifier, SyntaxFactory.IdentifierName("value")))); - var setAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, setValue); - var realAccessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {getAccessor, setAccessor})); - var realDecl = SyntaxFactory.PropertyDeclaration( - SyntaxFactory.List(attributes), - modifiers, - rawType, - null, - csIdentifier, realAccessors, - null, - null, - SyntaxFactory.Token(SyntaxKind.None)); - - _additionalDeclarations.Add(node, new MemberDeclarationSyntax[] { realDecl }); + string csIndentifierName = AddRealPropertyDelegatingToMyClassVersion(node, csIdentifier, attributes, modifiers, rawType); modifiers = modifiers.Remove(modifiers.Single(m => m.IsKind(SyntaxKind.VirtualKeyword))); csIdentifier = SyntaxFactory.Identifier(csIndentifierName); } @@ -646,6 +604,80 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme } } + private string AddRealPropertyDelegatingToMyClassVersion(VBSyntax.PropertyStatementSyntax node, SyntaxToken csIdentifier, + AttributeListSyntax[] attributes, SyntaxTokenList modifiers, TypeSyntax rawType) + { + var csIndentifierName = "MyClass" + csIdentifier.ValueText; + ExpressionSyntax thisDotIdentifier = SyntaxFactory.ParseExpression($"this.{csIndentifierName}"); + var getReturn = SyntaxFactory.Block(SyntaxFactory.ReturnStatement(thisDotIdentifier)); + var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, getReturn); + var setValue = SyntaxFactory.Block(SyntaxFactory.ExpressionStatement( + SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, thisDotIdentifier, + SyntaxFactory.IdentifierName("value")))); + var setAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration, setValue); + var realAccessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {getAccessor, setAccessor})); + var realDecl = SyntaxFactory.PropertyDeclaration( + SyntaxFactory.List(attributes), + modifiers, + rawType, + null, + csIdentifier, realAccessors, + null, + null, + SyntaxFactory.Token(SyntaxKind.None)); + + _additionalDeclarations.Add(node, new MemberDeclarationSyntax[] {realDecl}); + return csIndentifierName; + } + + private AccessorListSyntax GetPropertyAccesors(VBSyntax.PropertyStatementSyntax node, bool hasBody, bool isWriteOnly, + bool isReadonly, bool isInInterface) + { + AccessorListSyntax accessors = null; + if (!hasBody) + { + var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithSemicolonToken(SemicolonToken); + var setAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithSemicolonToken(SemicolonToken); + if (isWriteOnly) + { + getAccessor = getAccessor.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); + } + + if (isReadonly) + { + setAccessor = setAccessor.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); + } + + if (isInInterface && isReadonly) + { + accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {getAccessor})); + } + else if (isInInterface && isWriteOnly) + { + accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {setAccessor})); + } + else + { + // In VB, there's a backing field which can always be read and written to even on ReadOnly/WriteOnly properties. + // Our conversion will rewrite usages of that field to use the property accessors which therefore must exist and be private at minimum. + accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {getAccessor, setAccessor})); + } + } + else + { + accessors = SyntaxFactory.AccessorList( + SyntaxFactory.List( + ((VBSyntax.PropertyBlockSyntax) node.Parent).Accessors.Select(a => + (AccessorDeclarationSyntax) a.Accept(TriviaConvertingVisitor)) + ) + ); + } + + return accessors; + } + public override CSharpSyntaxNode VisitPropertyBlock(VBSyntax.PropertyBlockSyntax node) { return node.PropertyStatement.Accept(TriviaConvertingVisitor); From ccf4e0a28f37a11a9c037afdaebc27fdf9b5e157 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Wed, 31 Jul 2019 22:45:51 +0100 Subject: [PATCH 03/12] Extract variables --- ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index e5051a37e..a542c68cb 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -576,12 +576,13 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme throw new NotImplementedException("MyClass indexing not implemented"); } + var parameters = SyntaxFactory.BracketedParameterList(SyntaxFactory.SeparatedList(node.ParameterList.Parameters.Select(p => (ParameterSyntax)p.Accept(TriviaConvertingVisitor)))); return SyntaxFactory.IndexerDeclaration( SyntaxFactory.List(attributes), modifiers, rawType, null, - SyntaxFactory.BracketedParameterList(SyntaxFactory.SeparatedList(node.ParameterList.Parameters.Select(p => (ParameterSyntax)p.Accept(TriviaConvertingVisitor)))), + parameters, accessors ); } else { @@ -592,6 +593,7 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme csIdentifier = SyntaxFactory.Identifier(csIndentifierName); } + var semicolonToken = SyntaxFactory.Token(initializer == null ? SyntaxKind.None : SyntaxKind.SemicolonToken); return SyntaxFactory.PropertyDeclaration( SyntaxFactory.List(attributes), modifiers, @@ -600,7 +602,7 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme csIdentifier, accessors, null, initializer, - SyntaxFactory.Token(initializer == null ? SyntaxKind.None : SyntaxKind.SemicolonToken)); + semicolonToken); } } From 741bc55cc4ce45ed98acf4dc4e04c1af86225fc9 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Thu, 1 Aug 2019 00:05:53 +0100 Subject: [PATCH 04/12] Create methods for parameterized properties --- .../CSharp/NodesVisitor.cs | 141 ++++++++++++------ 1 file changed, 93 insertions(+), 48 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index a542c68cb..5fddfc3e8 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -548,7 +548,6 @@ string GetCSharpIdentifierText(VBasic.HandledEvent p) public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStatementSyntax node) { - bool hasBody = node.Parent is VBSyntax.PropertyBlockSyntax; var attributes = node.AttributeLists.SelectMany(ConvertAttribute).ToArray(); var isReadonly = node.Modifiers.Any(m => SyntaxTokenExtensions.IsKind(m, VBasic.SyntaxKind.ReadOnlyKeyword)); var isWriteOnly = node.Modifiers.Any(m => SyntaxTokenExtensions.IsKind(m, VBasic.SyntaxKind.WriteOnlyKeyword)); @@ -568,7 +567,27 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme _ => { throw new NotImplementedException($"{_.GetType().FullName} not implemented!"); } )?.Accept(TriviaConvertingVisitor) ?? VarType; - AccessorListSyntax accessors = GetPropertyAccesors(node, hasBody, isWriteOnly, isReadonly, isInInterface); + AccessorListSyntax accessors = null; + if (node.Parent is VBSyntax.PropertyBlockSyntax propertyBlock) { + if (!isIndexer && node.ParameterList?.Parameters.Any() != true) { + accessors = SyntaxFactory.AccessorList( + SyntaxFactory.List( + (propertyBlock.Accessors.Select(a => + (AccessorDeclarationSyntax)a.Accept(TriviaConvertingVisitor)) + ) + )); + } else { + //TODO Handled MyClass on a parameterized property here + var accessorMethods = propertyBlock.Accessors.Select(a => + (MethodDeclarationSyntax) a.Accept(TriviaConvertingVisitor)) + .Select(m => WithMergedModifiers(m)).ToArray(); + _additionalDeclarations.Add(node, accessorMethods.Skip(1).ToArray()); + return accessorMethods[0]; + } + } else { + accessors = ConvertSimpleAccessors(isWriteOnly, isReadonly, isInInterface); + } + if (isIndexer) { if (accessedThroughMyClass) { @@ -587,6 +606,7 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme ); } else { var csIdentifier = ConvertIdentifier(node.Identifier); + if (accessedThroughMyClass) { string csIndentifierName = AddRealPropertyDelegatingToMyClassVersion(node, csIdentifier, attributes, modifiers, rawType); modifiers = modifiers.Remove(modifiers.Single(m => m.IsKind(SyntaxKind.VirtualKeyword))); @@ -604,6 +624,14 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme initializer, semicolonToken); } + + MemberDeclarationSyntax WithMergedModifiers(MethodDeclarationSyntax member) + { + SyntaxTokenList originalModifiers = member.GetModifiers(); + var hasVisibility = originalModifiers.Any(m => m.IsCsVisibility(false, false)); + var modifiersToAdd = hasVisibility ? modifiers.Where(m => !m.IsCsVisibility(false, false)) : modifiers; + return member.WithModifiers(SyntaxFactory.TokenList(originalModifiers.Concat(modifiersToAdd))); + } } private string AddRealPropertyDelegatingToMyClassVersion(VBSyntax.PropertyStatementSyntax node, SyntaxToken csIdentifier, @@ -632,49 +660,36 @@ private string AddRealPropertyDelegatingToMyClassVersion(VBSyntax.PropertyStatem return csIndentifierName; } - private AccessorListSyntax GetPropertyAccesors(VBSyntax.PropertyStatementSyntax node, bool hasBody, bool isWriteOnly, - bool isReadonly, bool isInInterface) + private static AccessorListSyntax ConvertSimpleAccessors(bool isWriteOnly, bool isReadonly, bool isInInterface) { - AccessorListSyntax accessors = null; - if (!hasBody) + AccessorListSyntax accessors; + var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) + .WithSemicolonToken(SemicolonToken); + var setAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) + .WithSemicolonToken(SemicolonToken); + if (isWriteOnly) { - var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) - .WithSemicolonToken(SemicolonToken); - var setAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) - .WithSemicolonToken(SemicolonToken); - if (isWriteOnly) - { - getAccessor = getAccessor.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); - } + getAccessor = getAccessor.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); + } - if (isReadonly) - { - setAccessor = setAccessor.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); - } + if (isReadonly) + { + setAccessor = setAccessor.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword)); + } - if (isInInterface && isReadonly) - { - accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {getAccessor})); - } - else if (isInInterface && isWriteOnly) - { - accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {setAccessor})); - } - else - { - // In VB, there's a backing field which can always be read and written to even on ReadOnly/WriteOnly properties. - // Our conversion will rewrite usages of that field to use the property accessors which therefore must exist and be private at minimum. - accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {getAccessor, setAccessor})); - } + if (isInInterface && isReadonly) + { + accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {getAccessor})); + } + else if (isInInterface && isWriteOnly) + { + accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {setAccessor})); } else { - accessors = SyntaxFactory.AccessorList( - SyntaxFactory.List( - ((VBSyntax.PropertyBlockSyntax) node.Parent).Accessors.Select(a => - (AccessorDeclarationSyntax) a.Accept(TriviaConvertingVisitor)) - ) - ); + // In VB, there's a backing field which can always be read and written to even on ReadOnly/WriteOnly properties. + // Our conversion will rewrite usages of that field to use the property accessors which therefore must exist and be private at minimum. + accessors = SyntaxFactory.AccessorList(SyntaxFactory.List(new[] {getAccessor, setAccessor})); } return accessors; @@ -694,13 +709,24 @@ public override CSharpSyntaxNode VisitAccessorBlock(VBSyntax.AccessorBlockSyntax var body = WithImplicitReturnStatements(node, convertedStatements, csReturnVariableOrNull); var attributes = ConvertAttributes(node.AccessorStatement.AttributeLists); var modifiers = CommonConversions.ConvertModifiers(node, node.AccessorStatement.Modifiers, TokenContext.Local); - + string potentialMethodId = null; + var containingProperty = node.GetAncestor()?.PropertyStatement; switch (node.Kind()) { case VBasic.SyntaxKind.GetAccessorBlock: blockKind = SyntaxKind.GetAccessorDeclaration; + potentialMethodId = $"get_{(containingProperty.Identifier.Text)}"; + + if (TryConvertAsParameterizedProperty(out var cSharpSyntaxNode)) { + return cSharpSyntaxNode; + } break; case VBasic.SyntaxKind.SetAccessorBlock: blockKind = SyntaxKind.SetAccessorDeclaration; + potentialMethodId = $"set_{(containingProperty.Identifier.Text)}"; + + if (TryConvertAsParameterizedProperty(out cSharpSyntaxNode)) { + return cSharpSyntaxNode; + } break; case VBasic.SyntaxKind.AddHandlerAccessorBlock: blockKind = SyntaxKind.AddAccessorDeclaration; @@ -710,21 +736,40 @@ public override CSharpSyntaxNode VisitAccessorBlock(VBSyntax.AccessorBlockSyntax break; case VBasic.SyntaxKind.RaiseEventAccessorBlock: blockKind = SyntaxKind.MethodDeclaration; - break; + var eventStatement = ((VBSyntax.EventBlockSyntax)node.Parent).EventStatement; + var eventName = ConvertIdentifier(eventStatement.Identifier).ValueText; + potentialMethodId = $"On{eventName}"; + var parameterListSyntax = (ParameterListSyntax)node.AccessorStatement.ParameterList.Accept(TriviaConvertingVisitor); + var predefinedTypeSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)); + return CreateMethodDeclarationSyntax(predefinedTypeSyntax, parameterListSyntax); default: throw new NotSupportedException(node.Kind().ToString()); } - if (blockKind == SyntaxKind.MethodDeclaration) { - var parameterListSyntax = (ParameterListSyntax) node.AccessorStatement.ParameterList.Accept(TriviaConvertingVisitor); - var eventStatement = ((VBSyntax.EventBlockSyntax)node.Parent).EventStatement; - var eventName = ConvertIdentifier(eventStatement.Identifier).ValueText; + return SyntaxFactory.AccessorDeclaration(blockKind, attributes, modifiers, body); + + bool TryConvertAsParameterizedProperty(out CSharpSyntaxNode methodDeclarationSyntax) + { + if (containingProperty.ParameterList?.Parameters.Any() == true && containingProperty.AsClause is VBSyntax.SimpleAsClauseSyntax asClause) + { + var parameterListSyntax = + (ParameterListSyntax) containingProperty?.ParameterList.Accept(TriviaConvertingVisitor); + var predefinedTypeSyntax = (TypeSyntax) asClause.Type.Accept(TriviaConvertingVisitor); + methodDeclarationSyntax = CreateMethodDeclarationSyntax(predefinedTypeSyntax, parameterListSyntax); + return true; + } + + methodDeclarationSyntax = null; + return false; + } + + MethodDeclarationSyntax CreateMethodDeclarationSyntax(TypeSyntax predefinedTypeSyntax, ParameterListSyntax parameterListSyntax) + { return SyntaxFactory.MethodDeclaration(attributes, modifiers, - SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), null, - SyntaxFactory.Identifier($"On{eventName}"), null, + predefinedTypeSyntax, null, + SyntaxFactory.Identifier(potentialMethodId), null, parameterListSyntax, SyntaxFactory.List(), body, null); } - return SyntaxFactory.AccessorDeclaration(blockKind, attributes, modifiers, body); } public override CSharpSyntaxNode VisitAccessorStatement(VBSyntax.AccessorStatementSyntax node) @@ -1123,7 +1168,7 @@ public override CSharpSyntaxNode VisitTypeParameterList(VBSyntax.TypeParameterLi public override CSharpSyntaxNode VisitParameterList(VBSyntax.ParameterListSyntax node) { var parameterSyntaxs = node.Parameters.Select(p => (ParameterSyntax)p.Accept(TriviaConvertingVisitor)); - if (node.Parent is VBSyntax.PropertyStatementSyntax) { + if (node.Parent is VBSyntax.PropertyStatementSyntax && node.Parent.GetModifiers().Any(m => m.IsKind(VBasic.SyntaxKind.DefaultKeyword))) { return SyntaxFactory.BracketedParameterList(SyntaxFactory.SeparatedList(parameterSyntaxs)); } return SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameterSyntaxs)); From 427808e51f015cc88395eeb95ae6c94be01eb5ea Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Thu, 1 Aug 2019 00:12:46 +0100 Subject: [PATCH 05/12] Factor out and use Text rather than ValueText --- .../CSharp/NodesVisitor.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index 5fddfc3e8..8f9a93a97 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Operations; using Microsoft.VisualBasic.CompilerServices; +using StringComparer = System.StringComparer; using SyntaxFactory = Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using SyntaxNodeExtensions = ICSharpCode.CodeConverter.Util.SyntaxNodeExtensions; using VBSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax; @@ -45,6 +46,7 @@ class NodesVisitor : VBasic.VisualBasicSyntaxVisitor public CommentConvertingNodesVisitor TriviaConvertingVisitor { get; } private bool _optionCompareText = false; private VisualBasicEqualityComparison _visualBasicEqualityComparison; + private static HashSet _accessedThroughMyClass; private CommonConversions CommonConversions { get; } @@ -254,6 +256,7 @@ var dummyClass public override CSharpSyntaxNode VisitClassBlock(VBSyntax.ClassBlockSyntax node) { + _accessedThroughMyClass = GetMyClassAccessedNames(node); var classStatement = node.ClassStatement; var attributes = ConvertAttributes(classStatement.AttributeLists); SplitTypeParameters(classStatement.TypeParameterList, out var parameters, out var constraints); @@ -879,18 +882,22 @@ private static bool IsAccessedThroughMyClass(SyntaxNode node, SyntaxToken identi if (symbol.IsVirtual && !symbol.IsAbstract) { var classBlock = node.Ancestors().OfType().FirstOrDefault(); if (classBlock != null) { - var memberAccesses = classBlock.DescendantNodes().OfType(); - accessedThroughMyClass = memberAccesses.Any(mae => { - bool isMyClass = mae.Expression is VBSyntax.MyClassExpressionSyntax; - bool namesMatch = mae.Name.Identifier.ValueText.Equals(identifier.ValueText, StringComparison.OrdinalIgnoreCase); - return isMyClass && namesMatch; - }); + accessedThroughMyClass = _accessedThroughMyClass.Contains(identifier.Text); } } return accessedThroughMyClass; } + private static HashSet GetMyClassAccessedNames(VBSyntax.ClassBlockSyntax classBlock) + { + var memberAccesses = classBlock.DescendantNodes().OfType(); + var accessedTextNames = new HashSet(memberAccesses + .Where(mae => mae.Expression is VBSyntax.MyClassExpressionSyntax) + .Select(mae => mae.Name.Identifier.Text), StringComparer.OrdinalIgnoreCase); + return accessedTextNames; + } + public override CSharpSyntaxNode VisitMethodStatement(VBSyntax.MethodStatementSyntax node) { var attributes = ConvertAttributes(node.AttributeLists); From 8fca86df4c6f6022596c408a4a2d85da3a7e03a3 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Thu, 1 Aug 2019 00:46:48 +0100 Subject: [PATCH 06/12] Deal with get case --- .../CSharp/NodesVisitor.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index 8f9a93a97..bc40b1d06 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -1879,10 +1879,6 @@ public override CSharpSyntaxNode VisitInvocationExpression(VBSyntax.InvocationEx return CreateElementAccess(); } - if (expressionSymbol != null && expressionSymbol.IsKind(SymbolKind.Property)) { - return convertedExpression; - } - if (invocationSymbol?.Name == nameof(Enumerable.ElementAtOrDefault) && !expressionSymbol.Equals(invocationSymbol)) { _extraUsingDirectives.Add(nameof(System) + "." + nameof(System.Linq)); convertedExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, convertedExpression, @@ -1897,7 +1893,12 @@ ExpressionSyntax ConvertInvocationSubExpression(out bool isElementAccess) IsArrayElementAccess(operation) || ProbablyNotAMethodCall(node, expressionSymbol, expressionReturnType); - return (ExpressionSyntax)node.Expression.Accept(TriviaConvertingVisitor); + var expr = node.Expression.Accept(TriviaConvertingVisitor); + var overrideIdentifier = GetIdentifierTextForParameterizedPropertyAccess(operation); + if (expr is IdentifierNameSyntax ins && overrideIdentifier != null) { + return ins.WithIdentifier(SyntaxFactory.Identifier(overrideIdentifier)); + } + return (ExpressionSyntax) expr; } CSharpSyntaxNode CreateElementAccess() @@ -1919,7 +1920,16 @@ CSharpSyntaxNode CreateElementAccess() private static bool IsPropertyElementAccess(IOperation operation) { - return operation is IPropertyReferenceOperation pro && pro.Arguments.Any(); + return operation is IPropertyReferenceOperation pro && pro.Arguments.Any() && VBasic.VisualBasicExtensions.IsDefault(pro.Property); + } + + private static string GetIdentifierTextForParameterizedPropertyAccess(IOperation operation) + { + if (operation is IPropertyReferenceOperation pro && pro.Arguments.Any() && + !VBasic.VisualBasicExtensions.IsDefault(pro.Property)) { + return pro.Property.IsWriteOnly ? pro.Property.SetMethod.Name : pro.Property.GetMethod.Name; + } + return null; } private static bool IsArrayElementAccess(IOperation operation) From 2ff3edd5e85238f5bfaa3d267a091317ec4e36b0 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Thu, 1 Aug 2019 00:47:02 +0100 Subject: [PATCH 07/12] Expand test to cover set --- Tests/CSharp/MemberTests.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Tests/CSharp/MemberTests.cs b/Tests/CSharp/MemberTests.cs index 5a9034a6e..8c735ee45 100644 --- a/Tests/CSharp/MemberTests.cs +++ b/Tests/CSharp/MemberTests.cs @@ -553,7 +553,7 @@ public void TestParameterizedProperty() Public Property FirstName As String Public Property LastName As String - Public ReadOnly Property FullName(ByVal lastNameFirst As Boolean) As String + Public Property FullName(ByVal lastNameFirst As Boolean, ByVal isFirst As Boolean) As String Get If lastNameFirst Then Return LastName & "" "" & FirstName @@ -561,17 +561,21 @@ Return LastName & "" "" & FirstName Return FirstName & "" "" & LastName End If End Get + Set + If isFirst Then FirstName = Value + End Set End Property Public Overrides Function ToString() As String - Return FullName(False) + FullName(False, True) = ""hello"" + Return FullName(False, True) End Function End Class", @"class TestClass { public string FirstName { get; set; } public string LastName { get; set; } - public string get_FullName(bool lastNameFirst) + public string get_FullName(bool lastNameFirst, bool isFirst) { if (lastNameFirst) return LastName + "" "" + FirstName; @@ -579,9 +583,16 @@ public string get_FullName(bool lastNameFirst) return FirstName + "" "" + LastName; } + public string set_FullName(bool lastNameFirst, bool isFirst, string value) + { + if (isFirst) + FirstName = value; + } + public override string ToString() { - return get_FullName(false); + set_FullName(false, true, ""hello""); + return get_FullName(false, true); } }"); } From 090d542148327bdd1e72ca76f786201e1f08e95b Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Thu, 1 Aug 2019 01:36:10 +0100 Subject: [PATCH 08/12] Handle calling setter --- .../CSharp/CommonConversions.cs | 16 +++++++++++ .../CSharp/MethodBodyVisitor.cs | 2 ++ .../CSharp/NodesVisitor.cs | 27 ++++++++++--------- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs b/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs index c2c608879..9afaf89e0 100644 --- a/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs +++ b/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Syntax; using ArgumentListSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ArgumentListSyntax; @@ -505,5 +506,20 @@ public static VariableDeclarationSyntax CreateVariableDeclarationAndAssignment(s SyntaxFactory.SingletonSeparatedList(variableDeclaratorSyntax)); return variableDeclarationSyntax; } + + public string GetParameterizedPropertyAccessMethod(IOperation operation, out ExpressionSyntax extraArg) + { + if (operation is IPropertyReferenceOperation pro && pro.Arguments.Any() && + !pro.Property.IsDefault()) { + var isSetter = pro.Parent.Kind == OperationKind.SimpleAssignment && pro.Parent.Children.First() == pro; + extraArg = isSetter + ? (ExpressionSyntax)_nodesVisitor.Visit(operation.Parent.Syntax.ChildNodes().ElementAt(1)) + : null; + return isSetter ? pro.Property.SetMethod.Name : pro.Property.GetMethod.Name; + } + + extraArg = null; + return null; + } } } \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/CSharp/MethodBodyVisitor.cs b/ICSharpCode.CodeConverter/CSharp/MethodBodyVisitor.cs index c46db3795..6f31cadda 100644 --- a/ICSharpCode.CodeConverter/CSharp/MethodBodyVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/MethodBodyVisitor.cs @@ -135,6 +135,8 @@ public override SyntaxList VisitExpressionStatement(VBSyntax.Ex public override SyntaxList VisitAssignmentStatement(VBSyntax.AssignmentStatementSyntax node) { var lhs = (ExpressionSyntax)node.Left.Accept(_nodesVisitor); + var lOperation = _semanticModel.GetOperation(node.Left); + if (CommonConversions.GetParameterizedPropertyAccessMethod(lOperation, out var _) != null) return SingleStatement(lhs); var rhs = (ExpressionSyntax)node.Right.Accept(_nodesVisitor); if (node.Left is VBSyntax.IdentifierNameSyntax id && diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index bc40b1d06..31134ffe5 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -1872,6 +1872,20 @@ public override CSharpSyntaxNode VisitInvocationExpression(VBSyntax.InvocationEx return csEquivalent; } + var overrideIdentifier = CommonConversions.GetParameterizedPropertyAccessMethod(operation, out var extraArg); + if (overrideIdentifier != null) { + var expr = node.Expression.Accept(TriviaConvertingVisitor); + if (expr is IdentifierNameSyntax ins) { + expr = ins.WithIdentifier(SyntaxFactory.Identifier(overrideIdentifier)); + } + + var args = ConvertArgumentListOrEmpty(node.ArgumentList); + if (extraArg != null) { + args = args.WithArguments(args.Arguments.Add(SyntaxFactory.Argument(extraArg))); + } + return SyntaxFactory.InvocationExpression((ExpressionSyntax) expr, args); + } + // VB doesn't have a specialized node for element access because the syntax is ambiguous. Instead, it just uses an invocation expression or dictionary access expression, then figures out using the semantic model which one is most likely intended. // https://github.com/dotnet/roslyn/blob/master/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb#L768 var convertedExpression = ConvertInvocationSubExpression(out var shouldBeElementAccess); @@ -1894,10 +1908,6 @@ ExpressionSyntax ConvertInvocationSubExpression(out bool isElementAccess) ProbablyNotAMethodCall(node, expressionSymbol, expressionReturnType); var expr = node.Expression.Accept(TriviaConvertingVisitor); - var overrideIdentifier = GetIdentifierTextForParameterizedPropertyAccess(operation); - if (expr is IdentifierNameSyntax ins && overrideIdentifier != null) { - return ins.WithIdentifier(SyntaxFactory.Identifier(overrideIdentifier)); - } return (ExpressionSyntax) expr; } @@ -1923,15 +1933,6 @@ private static bool IsPropertyElementAccess(IOperation operation) return operation is IPropertyReferenceOperation pro && pro.Arguments.Any() && VBasic.VisualBasicExtensions.IsDefault(pro.Property); } - private static string GetIdentifierTextForParameterizedPropertyAccess(IOperation operation) - { - if (operation is IPropertyReferenceOperation pro && pro.Arguments.Any() && - !VBasic.VisualBasicExtensions.IsDefault(pro.Property)) { - return pro.Property.IsWriteOnly ? pro.Property.SetMethod.Name : pro.Property.GetMethod.Name; - } - return null; - } - private static bool IsArrayElementAccess(IOperation operation) { return operation != null && operation.Kind == OperationKind.ArrayElementReference; From d271a953b2d8471df04d63a013b381ce0fe64ea3 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Thu, 1 Aug 2019 01:36:22 +0100 Subject: [PATCH 09/12] Handle creating setter --- .../CSharp/NodesVisitor.cs | 30 +++++++++---------- Tests/CSharp/MemberTests.cs | 5 ++-- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index 31134ffe5..0a04fba45 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -583,8 +583,8 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme //TODO Handled MyClass on a parameterized property here var accessorMethods = propertyBlock.Accessors.Select(a => (MethodDeclarationSyntax) a.Accept(TriviaConvertingVisitor)) - .Select(m => WithMergedModifiers(m)).ToArray(); - _additionalDeclarations.Add(node, accessorMethods.Skip(1).ToArray()); + .Select(WithMergedModifiers).ToArray(); + _additionalDeclarations.Add(propertyBlock, accessorMethods.Skip(1).ToArray()); return accessorMethods[0]; } } else { @@ -719,16 +719,18 @@ public override CSharpSyntaxNode VisitAccessorBlock(VBSyntax.AccessorBlockSyntax blockKind = SyntaxKind.GetAccessorDeclaration; potentialMethodId = $"get_{(containingProperty.Identifier.Text)}"; - if (TryConvertAsParameterizedProperty(out var cSharpSyntaxNode)) { - return cSharpSyntaxNode; + if (containingProperty.AsClause is VBSyntax.SimpleAsClauseSyntax getAsClause && + TryConvertAsParameterizedProperty(out var method)) { + return method.WithReturnType((TypeSyntax)getAsClause.Type.Accept(TriviaConvertingVisitor)); } break; case VBasic.SyntaxKind.SetAccessorBlock: blockKind = SyntaxKind.SetAccessorDeclaration; potentialMethodId = $"set_{(containingProperty.Identifier.Text)}"; - - if (TryConvertAsParameterizedProperty(out cSharpSyntaxNode)) { - return cSharpSyntaxNode; + + if (containingProperty.AsClause is VBSyntax.SimpleAsClauseSyntax setAsClause && TryConvertAsParameterizedProperty(out var setMethod)) { + var valueParameterType = (TypeSyntax)setAsClause.Type.Accept(TriviaConvertingVisitor); + return setMethod.AddParameterListParameters(SyntaxFactory.Parameter(SyntaxFactory.Identifier("value")).WithType(valueParameterType)); } break; case VBasic.SyntaxKind.AddHandlerAccessorBlock: @@ -743,22 +745,20 @@ public override CSharpSyntaxNode VisitAccessorBlock(VBSyntax.AccessorBlockSyntax var eventName = ConvertIdentifier(eventStatement.Identifier).ValueText; potentialMethodId = $"On{eventName}"; var parameterListSyntax = (ParameterListSyntax)node.AccessorStatement.ParameterList.Accept(TriviaConvertingVisitor); - var predefinedTypeSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)); - return CreateMethodDeclarationSyntax(predefinedTypeSyntax, parameterListSyntax); + return CreateMethodDeclarationSyntax(parameterListSyntax); default: throw new NotSupportedException(node.Kind().ToString()); } return SyntaxFactory.AccessorDeclaration(blockKind, attributes, modifiers, body); - bool TryConvertAsParameterizedProperty(out CSharpSyntaxNode methodDeclarationSyntax) + bool TryConvertAsParameterizedProperty(out MethodDeclarationSyntax methodDeclarationSyntax) { - if (containingProperty.ParameterList?.Parameters.Any() == true && containingProperty.AsClause is VBSyntax.SimpleAsClauseSyntax asClause) + if (containingProperty.ParameterList?.Parameters.Any() == true) { var parameterListSyntax = (ParameterListSyntax) containingProperty?.ParameterList.Accept(TriviaConvertingVisitor); - var predefinedTypeSyntax = (TypeSyntax) asClause.Type.Accept(TriviaConvertingVisitor); - methodDeclarationSyntax = CreateMethodDeclarationSyntax(predefinedTypeSyntax, parameterListSyntax); + methodDeclarationSyntax = CreateMethodDeclarationSyntax(parameterListSyntax); return true; } @@ -766,10 +766,10 @@ bool TryConvertAsParameterizedProperty(out CSharpSyntaxNode methodDeclarationSyn return false; } - MethodDeclarationSyntax CreateMethodDeclarationSyntax(TypeSyntax predefinedTypeSyntax, ParameterListSyntax parameterListSyntax) + MethodDeclarationSyntax CreateMethodDeclarationSyntax(ParameterListSyntax parameterListSyntax) { return SyntaxFactory.MethodDeclaration(attributes, modifiers, - predefinedTypeSyntax, null, + SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), null, SyntaxFactory.Identifier(potentialMethodId), null, parameterListSyntax, SyntaxFactory.List(), body, null); } diff --git a/Tests/CSharp/MemberTests.cs b/Tests/CSharp/MemberTests.cs index 8c735ee45..ad676cdc5 100644 --- a/Tests/CSharp/MemberTests.cs +++ b/Tests/CSharp/MemberTests.cs @@ -561,7 +561,8 @@ Return LastName & "" "" & FirstName Return FirstName & "" "" & LastName End If End Get - Set + + Friend Set If isFirst Then FirstName = Value End Set End Property @@ -583,7 +584,7 @@ public string get_FullName(bool lastNameFirst, bool isFirst) return FirstName + "" "" + LastName; } - public string set_FullName(bool lastNameFirst, bool isFirst, string value) + internal void set_FullName(bool lastNameFirst, bool isFirst, string value) { if (isFirst) FirstName = value; From 43dfb1d90f31f1be292522996d75fc65ae31a792 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Thu, 1 Aug 2019 01:42:52 +0100 Subject: [PATCH 10/12] Tidy up --- ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index 0a04fba45..ed7e329e8 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -580,7 +580,7 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme ) )); } else { - //TODO Handled MyClass on a parameterized property here + //Logic error: MyClass on a parameterized property should be handled here by creating delegating methods var accessorMethods = propertyBlock.Accessors.Select(a => (MethodDeclarationSyntax) a.Accept(TriviaConvertingVisitor)) .Select(WithMergedModifiers).ToArray(); @@ -740,7 +740,6 @@ public override CSharpSyntaxNode VisitAccessorBlock(VBSyntax.AccessorBlockSyntax blockKind = SyntaxKind.RemoveAccessorDeclaration; break; case VBasic.SyntaxKind.RaiseEventAccessorBlock: - blockKind = SyntaxKind.MethodDeclaration; var eventStatement = ((VBSyntax.EventBlockSyntax)node.Parent).EventStatement; var eventName = ConvertIdentifier(eventStatement.Identifier).ValueText; potentialMethodId = $"On{eventName}"; From 1a6268289aad43e6f3250eb0c6e8d2c5376e58a4 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Thu, 1 Aug 2019 01:54:04 +0100 Subject: [PATCH 11/12] Guard against other property types --- .../CSharp/CommonConversions.cs | 51 ++++++++++--------- .../CSharp/NodesVisitor.cs | 10 ++-- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs b/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs index 9afaf89e0..32da206e1 100644 --- a/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs +++ b/ICSharpCode.CodeConverter/CSharp/CommonConversions.cs @@ -176,46 +176,46 @@ internal ExpressionSyntax GetLiteralExpression(object value, string textForUser { if (value is string valueTextForCompiler) { textForUser = GetQuotedStringTextForUser(textForUser, valueTextForCompiler); - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.StringLiteralExpression, + return SyntaxFactory.LiteralExpression(CSSyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(textForUser, valueTextForCompiler)); } if (value == null) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NullLiteralExpression); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NullLiteralExpression); if (value is bool) - return SyntaxFactory.LiteralExpression((bool)value ? Microsoft.CodeAnalysis.CSharp.SyntaxKind.TrueLiteralExpression : Microsoft.CodeAnalysis.CSharp.SyntaxKind.FalseLiteralExpression); + return SyntaxFactory.LiteralExpression((bool)value ? CSSyntaxKind.TrueLiteralExpression : CSSyntaxKind.FalseLiteralExpression); textForUser = textForUser != null ? ConvertNumericLiteralValueText(textForUser, value) : value.ToString(); if (value is byte) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (byte)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (byte)value)); if (value is sbyte) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (sbyte)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (sbyte)value)); if (value is short) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (short)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (short)value)); if (value is ushort) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (ushort)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (ushort)value)); if (value is int) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (int)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (int)value)); if (value is uint) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (uint)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (uint)value)); if (value is long) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (long)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (long)value)); if (value is ulong) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (ulong)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (ulong)value)); if (value is float) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (float)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (float)value)); if (value is double) { // Important to use value text, otherwise "10.0" gets coerced to and integer literal of 10 which can change semantics - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (double)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (double)value)); } if (value is decimal) { - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (decimal)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(textForUser, (decimal)value)); } if (value is char) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.CharacterLiteralExpression, SyntaxFactory.Literal((char)value)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.CharacterLiteralExpression, SyntaxFactory.Literal((char)value)); if (value is DateTime dt) { var valueToOutput = dt.Date.Equals(dt) ? dt.ToString("yyyy-MM-dd") : dt.ToString("yyyy-MM-dd HH:mm:ss"); @@ -351,7 +351,7 @@ public static bool InMethodCalledInitializeComponent(SyntaxNode anyNodePossiblyW private static SyntaxToken CsEscapedIdentifier(string text) { - if (SyntaxFacts.GetKeywordKind(text) != Microsoft.CodeAnalysis.CSharp.SyntaxKind.None) text = "@" + text; + if (SyntaxFacts.GetKeywordKind(text) != CSSyntaxKind.None) text = "@" + text; return SyntaxFactory.Identifier(text); } @@ -365,7 +365,7 @@ public SyntaxTokenList ConvertModifiers(SyntaxNode node, IEnumerable CSharpExtensions.Kind(t) != Microsoft.CodeAnalysis.CSharp.SyntaxKind.None)); + return SyntaxFactory.TokenList(ConvertModifiersCore(declaredAccessibility, modifiers, context).Where(t => CSharpExtensions.Kind(t) != CSSyntaxKind.None)); } private SyntaxToken? ConvertModifier(SyntaxToken m, TokenContext context = TokenContext.Global) @@ -376,7 +376,7 @@ public SyntaxTokenList ConvertModifiers(SyntaxNode node, IEnumerable ConvertModifiersCore(Accessibility declaredAccessibility, @@ -396,7 +396,7 @@ private IEnumerable ConvertModifiersCore(Accessibility declaredAcce } if (context == TokenContext.MemberInModule && !remainingModifiers.Any(a => VisualBasicExtensions.Kind(a) == SyntaxKind.ConstKeyword )) - yield return SyntaxFactory.Token(Microsoft.CodeAnalysis.CSharp.SyntaxKind.StaticKeyword); + yield return SyntaxFactory.Token(CSSyntaxKind.StaticKeyword); } private IEnumerable CsSyntaxAccessibilityKindForContext(Accessibility declaredAccessibility, @@ -441,7 +441,7 @@ private bool IgnoreInContext(SyntaxToken m, TokenContext context) public bool IsConversionOperator(SyntaxToken token) { - bool isConvOp= token.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExplicitKeyword, Microsoft.CodeAnalysis.CSharp.SyntaxKind.ImplicitKeyword) + bool isConvOp= token.IsKind(CSSyntaxKind.ExplicitKeyword, CSSyntaxKind.ImplicitKeyword) ||token.IsKind(SyntaxKind.NarrowingKeyword, SyntaxKind.WideningKeyword); return isConvOp; } @@ -483,11 +483,11 @@ private ExpressionSyntax IncreaseArrayUpperBoundExpression(VBSyntax.ExpressionSy { var constant = _semanticModel.GetConstantValue(expr); if (constant.HasValue && constant.Value is int) - return SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal((int)constant.Value + 1)); + return SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal((int)constant.Value + 1)); return SyntaxFactory.BinaryExpression( - Microsoft.CodeAnalysis.CSharp.SyntaxKind.SubtractExpression, - (ExpressionSyntax)expr.Accept(_nodesVisitor), SyntaxFactory.Token(Microsoft.CodeAnalysis.CSharp.SyntaxKind.PlusToken), SyntaxFactory.LiteralExpression(Microsoft.CodeAnalysis.CSharp.SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(1))); + CSSyntaxKind.SubtractExpression, + (ExpressionSyntax)expr.Accept(_nodesVisitor), SyntaxFactory.Token(CSSyntaxKind.PlusToken), SyntaxFactory.LiteralExpression(CSSyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(1))); } public static AttributeArgumentListSyntax CreateAttributeArgumentList(params AttributeArgumentSyntax[] attributeArgumentSyntaxs) @@ -521,5 +521,10 @@ public string GetParameterizedPropertyAccessMethod(IOperation operation, out Exp extraArg = null; return null; } + + public static bool IsDefaultIndexer(SyntaxNode node) + { + return node is PropertyStatementSyntax pss && pss.Modifiers.Any(m => SyntaxTokenExtensions.IsKind(m, Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.DefaultKeyword)); + } } } \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index ed7e329e8..bfa4e19ea 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -556,7 +556,7 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme var isWriteOnly = node.Modifiers.Any(m => SyntaxTokenExtensions.IsKind(m, VBasic.SyntaxKind.WriteOnlyKeyword)); var convertibleModifiers = node.Modifiers.Where(m => !m.IsKind(VBasic.SyntaxKind.ReadOnlyKeyword, VBasic.SyntaxKind.WriteOnlyKeyword, VBasic.SyntaxKind.DefaultKeyword)); var modifiers = CommonConversions.ConvertModifiers(node, convertibleModifiers, GetMemberContext(node)); - var isIndexer = node.Modifiers.Any(m => SyntaxTokenExtensions.IsKind(m, VBasic.SyntaxKind.DefaultKeyword)); + var isIndexer = CommonConversions.IsDefaultIndexer(node); var accessedThroughMyClass = IsAccessedThroughMyClass(node, node.Identifier, _semanticModel.GetDeclaredSymbol(node)); bool isInInterface = node.Ancestors().OfType().FirstOrDefault() != null; @@ -753,7 +753,7 @@ public override CSharpSyntaxNode VisitAccessorBlock(VBSyntax.AccessorBlockSyntax bool TryConvertAsParameterizedProperty(out MethodDeclarationSyntax methodDeclarationSyntax) { - if (containingProperty.ParameterList?.Parameters.Any() == true) + if (containingProperty.ParameterList?.Parameters.Any() == true && !CommonConversions.IsDefaultIndexer(containingProperty)) { var parameterListSyntax = (ParameterListSyntax) containingProperty?.ParameterList.Accept(TriviaConvertingVisitor); @@ -1174,7 +1174,7 @@ public override CSharpSyntaxNode VisitTypeParameterList(VBSyntax.TypeParameterLi public override CSharpSyntaxNode VisitParameterList(VBSyntax.ParameterListSyntax node) { var parameterSyntaxs = node.Parameters.Select(p => (ParameterSyntax)p.Accept(TriviaConvertingVisitor)); - if (node.Parent is VBSyntax.PropertyStatementSyntax && node.Parent.GetModifiers().Any(m => m.IsKind(VBasic.SyntaxKind.DefaultKeyword))) { + if (node.Parent is VBSyntax.PropertyStatementSyntax && CommonConversions.IsDefaultIndexer(node.Parent)) { return SyntaxFactory.BracketedParameterList(SyntaxFactory.SeparatedList(parameterSyntaxs)); } return SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameterSyntaxs)); @@ -1892,6 +1892,10 @@ public override CSharpSyntaxNode VisitInvocationExpression(VBSyntax.InvocationEx return CreateElementAccess(); } + if (expressionSymbol != null && expressionSymbol.IsKind(SymbolKind.Property)) { + return convertedExpression; //Parameterless property access + } + if (invocationSymbol?.Name == nameof(Enumerable.ElementAtOrDefault) && !expressionSymbol.Equals(invocationSymbol)) { _extraUsingDirectives.Add(nameof(System) + "." + nameof(System.Linq)); convertedExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, convertedExpression, From 22fb8a58bd2e64da2664ac942bdde69d97ecd4a2 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Thu, 1 Aug 2019 02:08:30 +0100 Subject: [PATCH 12/12] Cater for complex index accessors --- .../CSharp/NodesVisitor.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs index bfa4e19ea..8fb1013c1 100644 --- a/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/CSharp/NodesVisitor.cs @@ -572,21 +572,24 @@ public override CSharpSyntaxNode VisitPropertyStatement(VBSyntax.PropertyStateme AccessorListSyntax accessors = null; if (node.Parent is VBSyntax.PropertyBlockSyntax propertyBlock) { - if (!isIndexer && node.ParameterList?.Parameters.Any() != true) { - accessors = SyntaxFactory.AccessorList( - SyntaxFactory.List( - (propertyBlock.Accessors.Select(a => - (AccessorDeclarationSyntax)a.Accept(TriviaConvertingVisitor)) - ) - )); - } else { - //Logic error: MyClass on a parameterized property should be handled here by creating delegating methods + if (node.ParameterList?.Parameters.Any() == true && !isIndexer) { + if (accessedThroughMyClass) { + // Would need to create a delegating implementation to implement this + throw new NotImplementedException("MyClass indexing not implemented"); + } var accessorMethods = propertyBlock.Accessors.Select(a => - (MethodDeclarationSyntax) a.Accept(TriviaConvertingVisitor)) + (MethodDeclarationSyntax)a.Accept(TriviaConvertingVisitor)) .Select(WithMergedModifiers).ToArray(); _additionalDeclarations.Add(propertyBlock, accessorMethods.Skip(1).ToArray()); return accessorMethods[0]; } + + accessors = SyntaxFactory.AccessorList( + SyntaxFactory.List( + (propertyBlock.Accessors.Select(a => + (AccessorDeclarationSyntax)a.Accept(TriviaConvertingVisitor)) + ) + )); } else { accessors = ConvertSimpleAccessors(isWriteOnly, isReadonly, isInInterface); }