From 3e38662094e655cdfc2dcc360ee14e3d1d00d8c2 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Mon, 11 Mar 2019 01:03:17 +0000 Subject: [PATCH 1/8] Need this for an exact match at least sometimes, so always output --- ICSharpCode.CodeConverter/VB/NodesVisitor.cs | 8 ++------ Tests/VB/SpecialConversionTests.cs | 7 +++++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs index e62354d0b..a43e9c0fe 100644 --- a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs @@ -1520,15 +1520,11 @@ private TypeSyntax GetOverloadedFormalParameterTypeOrNull(CSS.ExpressionSyntax a var argIndex = ies.ArgumentList.Arguments.IndexOf(nameArgument); //TODO: Deal with named parameters var symbolInfo = _semanticModel.GetSymbolInfo(ies.Expression); - // We ignore symbolInfo.Symbol, since if there's an exact match it isn't overloaded - var destinationType = symbolInfo.CandidateSymbols - .Select(m => m.GetParameters()).Where(p => p.Length > argIndex).Select(p => p[argIndex].Type) - .FirstOrDefault(); - + var destinationType = symbolInfo.ExtractBestMatch(m => m.GetParameters().Length > argIndex); if (destinationType != null) { var toCreate = (TypeSyntax) CS.SyntaxFactory - .ParseTypeName(destinationType.ToMinimalDisplayString(_semanticModel, + .ParseTypeName(destinationType.GetParameters()[argIndex].Type.ToMinimalDisplayString(_semanticModel, argumentChildExpression.SpanStart)) .Accept(TriviaConvertingVisitor); return toCreate; diff --git a/Tests/VB/SpecialConversionTests.cs b/Tests/VB/SpecialConversionTests.cs index 5e83e6d84..290a034d4 100644 --- a/Tests/VB/SpecialConversionTests.cs +++ b/Tests/VB/SpecialConversionTests.cs @@ -218,7 +218,9 @@ End Sub [Fact] public void AddressOfWhereVbTypeInferenceIsWeaker() { - TestConversionCSharpToVisualBasic(@"static class TestClass + TestConversionCSharpToVisualBasic(@"using System; + +static class TestClass { private static object TypeSwitch(this object obj, Func matchFunc1, Func matchFunc2, Func defaultFunc) { @@ -239,7 +241,8 @@ public static object Convert(object node) { return node.TypeSwitch(ConvertString, ConvertInt, _ => throw new NotImplementedException($""Conversion for '{node.GetType()}' not implemented"")); } -}", @"Imports System.Runtime.CompilerServices +}", @"Imports System +Imports System.Runtime.CompilerServices Friend Module TestClass From 4b04ad722fa9d1fe33123385be797bf9def13c90 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Mon, 11 Mar 2019 01:29:01 +0000 Subject: [PATCH 2/8] Refactor code for RaiseEvent --- .../VB/MethodBodyVisitor.cs | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs b/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs index 4832b2625..b398d9910 100644 --- a/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs @@ -85,10 +85,9 @@ public override SyntaxList VisitExpressionStatement(CSS.Express public override SyntaxList VisitIfStatement(CSS.IfStatementSyntax node) { - IdentifierNameSyntax name; List arguments = new List(); StatementSyntax stmt; - if (node.Else == null && TryConvertRaiseEvent(node, out name, arguments)) { + if (node.Else == null && TryConvertIfNotNullRaiseEvent(node, out IdentifierNameSyntax name, arguments)) { stmt = SyntaxFactory.RaiseEventStatement(name, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))); return SyntaxFactory.SingletonList(stmt); } @@ -131,37 +130,51 @@ bool IsSimpleStatement(CSS.StatementSyntax statement) || statement is CSS.ThrowStatementSyntax; } - bool TryConvertRaiseEvent(CSS.IfStatementSyntax node, out IdentifierNameSyntax name, List arguments) + bool TryConvertIfNotNullRaiseEvent(CSS.IfStatementSyntax node, out IdentifierNameSyntax name, List arguments) { name = null; + if (TrimParenthesis(node) is CSS.BinaryExpressionSyntax be + && be.IsKind(CS.SyntaxKind.NotEqualsExpression) + && (be.Left.IsKind(CS.SyntaxKind.NullLiteralExpression) || + be.Right.IsKind(CS.SyntaxKind.NullLiteralExpression))) { + return TryConvertRaiseEvent(ref name, arguments, node.Statement, be); + } + return false; + } + + private static CSS.ExpressionSyntax TrimParenthesis(CSS.IfStatementSyntax node) + { var condition = node.Condition; - while (condition is CSS.ParenthesizedExpressionSyntax) - condition = ((CSS.ParenthesizedExpressionSyntax)condition).Expression; - if (!(condition is CSS.BinaryExpressionSyntax)) - return false; - var be = (CSS.BinaryExpressionSyntax)condition; - if (!be.IsKind(CS.SyntaxKind.NotEqualsExpression) || (!be.Left.IsKind(CS.SyntaxKind.NullLiteralExpression) && !be.Right.IsKind(CS.SyntaxKind.NullLiteralExpression))) - return false; + while (condition is CSS.ParenthesizedExpressionSyntax pExp) condition = pExp.Expression; + return condition; + } + + private bool TryConvertRaiseEvent(ref IdentifierNameSyntax name, List arguments, CSS.StatementSyntax resultStatement, + CSS.BinaryExpressionSyntax be) + { CSS.ExpressionStatementSyntax singleStatement; - if (node.Statement is CSS.BlockSyntax) { - var block = ((CSS.BlockSyntax)node.Statement); + if (resultStatement is CSS.BlockSyntax block) + { if (block.Statements.Count != 1) return false; singleStatement = block.Statements[0] as CSS.ExpressionStatementSyntax; - } else { - singleStatement = node.Statement as CSS.ExpressionStatementSyntax; } + else + { + singleStatement = resultStatement as CSS.ExpressionStatementSyntax; + } + if (singleStatement == null || !(singleStatement.Expression is CSS.InvocationExpressionSyntax)) return false; var possibleEventName = GetPossibleEventName(be.Left) ?? GetPossibleEventName(be.Right); if (possibleEventName == null) return false; - var invocation = (CSS.InvocationExpressionSyntax)singleStatement.Expression; + var invocation = (CSS.InvocationExpressionSyntax) singleStatement.Expression; var invocationName = GetPossibleEventName(invocation.Expression); if (possibleEventName != invocationName) return false; name = SyntaxFactory.IdentifierName(possibleEventName); - arguments.AddRange(invocation.ArgumentList.Arguments.Select(a => (ArgumentSyntax)a.Accept(_nodesVisitor))); + arguments.AddRange(invocation.ArgumentList.Arguments.Select(a => (ArgumentSyntax) a.Accept(_nodesVisitor))); return true; } From f41bc9c1698ba69ff17e0ea0d7a1c8357f1da52a Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Mon, 11 Mar 2019 02:19:14 +0000 Subject: [PATCH 3/8] Convert RaiseEvent correctly Still need to do converter for Foo?.Invoke And for general case of switching to say FooEvent rather than Foo (unless actually raising an event) --- .../VB/CommonConversions.cs | 9 +- .../VB/MethodBodyVisitor.cs | 100 ++++++------ ICSharpCode.CodeConverter/VB/NodesVisitor.cs | 42 ++--- Tests/TestRunners/ConverterTestBase.cs | 2 +- Tests/VB/SpecialConversionTests.cs | 143 +++++++++++++++--- 5 files changed, 208 insertions(+), 88 deletions(-) diff --git a/ICSharpCode.CodeConverter/VB/CommonConversions.cs b/ICSharpCode.CodeConverter/VB/CommonConversions.cs index b6ca8c5f1..979b413fd 100644 --- a/ICSharpCode.CodeConverter/VB/CommonConversions.cs +++ b/ICSharpCode.CodeConverter/VB/CommonConversions.cs @@ -396,12 +396,12 @@ public VisualBasicSyntaxNode ConvertTopLevelExpression(Microsoft.CodeAnalysis.CS return topLevelExpression.Accept(_nodesVisitor); } - private static ModifiedIdentifierSyntax ExtractIdentifier(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclaratorSyntax v) + private ModifiedIdentifierSyntax ExtractIdentifier(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclaratorSyntax v) { return SyntaxFactory.ModifiedIdentifier(ConvertIdentifier(v.Identifier)); } - public static SyntaxToken ConvertIdentifier(SyntaxToken id) + public SyntaxToken ConvertIdentifier(SyntaxToken id) { var idText = id.ValueText; // Underscore is a special character in VB lexer which continues lines - not sure where to find the whole set of other similar tokens if any @@ -530,5 +530,10 @@ private static string UppercaseFirstLetter(string sourceText) { return sourceText.Substring(0, 1).ToUpper() + sourceText.Substring(1); } + + public bool IsEventHandlerType(CSharpSyntaxNode syntax) + { + return _semanticModel.GetTypeInfo(syntax).Type?.GetFullMetadataName()?.StartsWith("System.EventHandler") == true; + } } } \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs b/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs index b398d9910..d14b91a6c 100644 --- a/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs @@ -41,6 +41,12 @@ class BlockInfo } public CommentConvertingMethodBodyVisitor CommentConvertingVisitor { get; } + public SemanticModel SemanticModel + { + set { _semanticModel = value; } + get { return _semanticModel; } + } + public MethodBodyVisitor(SemanticModel semanticModel, CSharpSyntaxVisitor nodesVisitor, TriviaConverter triviaConverter, CommonConversions commonConversions) @@ -85,15 +91,17 @@ public override SyntaxList VisitExpressionStatement(CSS.Express public override SyntaxList VisitIfStatement(CSS.IfStatementSyntax node) { - List arguments = new List(); - StatementSyntax stmt; - if (node.Else == null && TryConvertIfNotNullRaiseEvent(node, out IdentifierNameSyntax name, arguments)) { - stmt = SyntaxFactory.RaiseEventStatement(name, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))); + if (node.Else == null && TryConvertIfNotNullRaiseEvent(node, out var stmt)) { return SyntaxFactory.SingletonList(stmt); } var elseIfBlocks = new List(); ElseBlockSyntax elseBlock = null; - CollectElseBlocks(node, elseIfBlocks, ref elseBlock); + if (TryConvertElseRaiseEvent(node, out var elseStmt)) { + elseBlock = SyntaxFactory.ElseBlock(SyntaxFactory.SingletonList(elseStmt)); + } else { + CollectElseBlocks(node, elseIfBlocks, ref elseBlock); + } + if (node.Statement is CSS.BlockSyntax) { stmt = SyntaxFactory.MultiLineIfBlock( SyntaxFactory.IfStatement((ExpressionSyntax)node.Condition.Accept(_nodesVisitor)).WithThenKeyword(SyntaxFactory.Token(SyntaxKind.ThenKeyword)), @@ -130,16 +138,28 @@ bool IsSimpleStatement(CSS.StatementSyntax statement) || statement is CSS.ThrowStatementSyntax; } - bool TryConvertIfNotNullRaiseEvent(CSS.IfStatementSyntax node, out IdentifierNameSyntax name, List arguments) + bool TryConvertIfNotNullRaiseEvent(CSS.IfStatementSyntax node, out StatementSyntax raiseEventStatement) { - name = null; - if (TrimParenthesis(node) is CSS.BinaryExpressionSyntax be - && be.IsKind(CS.SyntaxKind.NotEqualsExpression) - && (be.Left.IsKind(CS.SyntaxKind.NullLiteralExpression) || - be.Right.IsKind(CS.SyntaxKind.NullLiteralExpression))) { - return TryConvertRaiseEvent(ref name, arguments, node.Statement, be); - } - return false; + raiseEventStatement = null; + return TryGetBinaryExpression(node, out var comparisonExpression, CS.SyntaxKind.NotEqualsExpression, CS.SyntaxKind.NullLiteralExpression) + && TryConvertRaiseEvent(node.Statement, comparisonExpression, ref raiseEventStatement); + } + + bool TryConvertElseRaiseEvent(CSS.IfStatementSyntax node, out StatementSyntax raiseEventStatement) + { + raiseEventStatement = null; + return node.Else != null && + TryGetBinaryExpression(node, out var comparisonExpression, CS.SyntaxKind.EqualsExpression, CS.SyntaxKind.NullLiteralExpression) + && TryConvertRaiseEvent(node.Else.Statement, comparisonExpression, ref raiseEventStatement); + } + + private static bool TryGetBinaryExpression(CSS.IfStatementSyntax node, out CSS.BinaryExpressionSyntax binaryExpressionSyntax, CS.SyntaxKind notEqualsExpression, CS.SyntaxKind operand) + { + binaryExpressionSyntax = TrimParenthesis(node) as CSS.BinaryExpressionSyntax; + return binaryExpressionSyntax != null + && binaryExpressionSyntax.IsKind(notEqualsExpression) + && (binaryExpressionSyntax.Left.IsKind(operand) || + binaryExpressionSyntax.Right.IsKind(operand)); } private static CSS.ExpressionSyntax TrimParenthesis(CSS.IfStatementSyntax node) @@ -149,8 +169,8 @@ private static CSS.ExpressionSyntax TrimParenthesis(CSS.IfStatementSyntax node) return condition; } - private bool TryConvertRaiseEvent(ref IdentifierNameSyntax name, List arguments, CSS.StatementSyntax resultStatement, - CSS.BinaryExpressionSyntax be) + private bool TryConvertRaiseEvent(CSS.StatementSyntax resultStatement, + CSS.BinaryExpressionSyntax be, ref StatementSyntax raiseEventStatement) { CSS.ExpressionStatementSyntax singleStatement; if (resultStatement is CSS.BlockSyntax block) @@ -166,29 +186,21 @@ private bool TryConvertRaiseEvent(ref IdentifierNameSyntax name, List (ArgumentSyntax) a.Accept(_nodesVisitor))); + var arguments = invocation.ArgumentList.Arguments.Select(a => (ArgumentSyntax) a.Accept(_nodesVisitor)); + raiseEventStatement = SyntaxFactory.RaiseEventStatement((IdentifierNameSyntax) nameIdentifier +, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))); return true; } - string GetPossibleEventName(CSS.ExpressionSyntax expression) - { - var ident = expression as CSS.IdentifierNameSyntax; - if (ident != null) - return ident.Identifier.Text; - var fre = expression as CSS.MemberAccessExpressionSyntax; - if (fre != null && fre.Expression.IsKind(CS.SyntaxKind.ThisExpression)) - return fre.Name.Identifier.Text; - return null; - } - void CollectElseBlocks(CSS.IfStatementSyntax node, List elseIfBlocks, ref ElseBlockSyntax elseBlock) { if (node.Else == null) return; @@ -201,8 +213,10 @@ void CollectElseBlocks(CSS.IfStatementSyntax node, List elseI ) ); CollectElseBlocks(elseIf, elseIfBlocks, ref elseBlock); - } else - elseBlock = SyntaxFactory.ElseBlock(ConvertBlock(node.Else.Statement)); + } else { + SyntaxList statements = ConvertBlock(node.Else.Statement); + elseBlock = SyntaxFactory.ElseBlock(statements); + } } public override SyntaxList VisitSwitchStatement(CSS.SwitchStatementSyntax node) @@ -376,7 +390,7 @@ bool ConvertForToSimpleForNext(CSS.ForStatementSyntax node, out StatementSyntax if (start == null) return false; variable = SyntaxFactory.VariableDeclarator( - SyntaxFactory.SingletonSeparatedList(SyntaxFactory.ModifiedIdentifier(CommonConversions.ConvertIdentifier(v.Identifier))), + SyntaxFactory.SingletonSeparatedList(SyntaxFactory.ModifiedIdentifier(_commonConversions.ConvertIdentifier(v.Identifier))), node.Declaration.Type.IsVar ? null : SyntaxFactory.SimpleAsClause((TypeSyntax)node.Declaration.Type.Accept(_nodesVisitor)), null ); @@ -424,13 +438,13 @@ public override SyntaxList VisitForEachStatement(CSS.ForEachSta VisualBasicSyntaxNode variable; if (node.Type.IsVar) { - variable = SyntaxFactory.IdentifierName(CommonConversions.ConvertIdentifier(node.Identifier)); + variable = SyntaxFactory.IdentifierName(_commonConversions.ConvertIdentifier(node.Identifier)); } else { variable = SyntaxFactory.VariableDeclarator( SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.ModifiedIdentifier(CommonConversions.ConvertIdentifier(node.Identifier))), + SyntaxFactory.ModifiedIdentifier(_commonConversions.ConvertIdentifier(node.Identifier))), SyntaxFactory.SimpleAsClause((TypeSyntax) node.Type.Accept(_nodesVisitor)), null ); @@ -476,7 +490,7 @@ CatchBlockSyntax ConvertCatchClause(int index, CSS.CatchClauseSyntax catchClause simpleTypeName = type.ToString(); return SyntaxFactory.CatchBlock( SyntaxFactory.CatchStatement( - SyntaxFactory.IdentifierName(SyntaxTokenExtensions.IsKind(catchClause.Declaration.Identifier, CS.SyntaxKind.None) ? SyntaxFactory.Identifier($"__unused{simpleTypeName}{index + 1}__") : CommonConversions.ConvertIdentifier(catchClause.Declaration.Identifier)), + SyntaxFactory.IdentifierName(SyntaxTokenExtensions.IsKind(catchClause.Declaration.Identifier, CS.SyntaxKind.None) ? SyntaxFactory.Identifier($"__unused{simpleTypeName}{index + 1}__") : _commonConversions.ConvertIdentifier(catchClause.Declaration.Identifier)), SyntaxFactory.SimpleAsClause(type), catchClause.Filter == null ? null : SyntaxFactory.CatchFilterClause((ExpressionSyntax)catchClause.Filter.FilterExpression.Accept(_nodesVisitor)) ), statements @@ -507,7 +521,7 @@ public override SyntaxList VisitLockStatement(CSS.LockStatement public override SyntaxList VisitLabeledStatement(CSS.LabeledStatementSyntax node) { - return SyntaxFactory.SingletonList(SyntaxFactory.LabelStatement(CommonConversions.ConvertIdentifier(node.Identifier))) + return SyntaxFactory.SingletonList(SyntaxFactory.LabelStatement(_commonConversions.ConvertIdentifier(node.Identifier))) .AddRange(ConvertBlock(node.Statement)); } @@ -531,7 +545,7 @@ public override SyntaxList VisitGotoStatement(CSS.GotoStatement _blockInfo.Peek().GotoCaseExpressions.Add(labelExpression); label = SyntaxFactory.Label(SyntaxKind.IdentifierLabel, MakeGotoSwitchLabel(labelExpression)); } else { - label = SyntaxFactory.Label(SyntaxKind.IdentifierLabel, CommonConversions.ConvertIdentifier(((CSS.IdentifierNameSyntax)node.Expression).Identifier)); + label = SyntaxFactory.Label(SyntaxKind.IdentifierLabel, _commonConversions.ConvertIdentifier(((CSS.IdentifierNameSyntax)node.Expression).Identifier)); } return SyntaxFactory.SingletonList(SyntaxFactory.GoToStatement(label)); } diff --git a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs index a43e9c0fe..9f47dff63 100644 --- a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs @@ -179,7 +179,7 @@ public override VisualBasicSyntaxNode VisitUsingDirective(CSS.UsingDirectiveSynt ImportAliasClauseSyntax alias = null; if (node.Alias != null) { var name = node.Alias.Name; - var id = CommonConversions.ConvertIdentifier(name.Identifier); + var id = _commonConversions.ConvertIdentifier(name.Identifier); alias = SyntaxFactory.ImportAliasClause(id); } ImportsClauseSyntax clause = SyntaxFactory.SimpleImportsClause(alias, (NameSyntax)node.Name.Accept(TriviaConvertingVisitor)); @@ -193,7 +193,7 @@ public override VisualBasicSyntaxNode VisitUsingDirective(CSS.UsingDirectiveSynt public override VisualBasicSyntaxNode VisitClassDeclaration(CSS.ClassDeclarationSyntax node) { var members = ConvertMembers(node).ToList(); - var id = CommonConversions.ConvertIdentifier(node.Identifier); + var id = _commonConversions.ConvertIdentifier(node.Identifier); List inherits = new List(); List implements = new List(); @@ -238,7 +238,7 @@ public override VisualBasicSyntaxNode VisitStructDeclaration(CSS.StructDeclarati return SyntaxFactory.StructureBlock( SyntaxFactory.StructureStatement( - SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))), CommonConversions.ConvertModifiers(node.Modifiers), CommonConversions.ConvertIdentifier(node.Identifier), + SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))), CommonConversions.ConvertModifiers(node.Modifiers), _commonConversions.ConvertIdentifier(node.Identifier), (TypeParameterListSyntax)node.TypeParameterList?.Accept(TriviaConvertingVisitor) ), SyntaxFactory.List(inherits), @@ -257,7 +257,7 @@ public override VisualBasicSyntaxNode VisitInterfaceDeclaration(CSS.InterfaceDec return SyntaxFactory.InterfaceBlock( SyntaxFactory.InterfaceStatement( - SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))), CommonConversions.ConvertModifiers(node.Modifiers, TokenContext.InterfaceOrModule), CommonConversions.ConvertIdentifier(node.Identifier), + SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))), CommonConversions.ConvertModifiers(node.Modifiers, TokenContext.InterfaceOrModule), _commonConversions.ConvertIdentifier(node.Identifier), (TypeParameterListSyntax)node.TypeParameterList?.Accept(TriviaConvertingVisitor) ), SyntaxFactory.List(inherits), @@ -273,7 +273,7 @@ public override VisualBasicSyntaxNode VisitEnumDeclaration(CSS.EnumDeclarationSy .Types.OfType().Single().Type.Accept(TriviaConvertingVisitor); return SyntaxFactory.EnumBlock( SyntaxFactory.EnumStatement( - SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))), CommonConversions.ConvertModifiers(node.Modifiers), CommonConversions.ConvertIdentifier(node.Identifier), + SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))), CommonConversions.ConvertModifiers(node.Modifiers), _commonConversions.ConvertIdentifier(node.Identifier), baseType == null ? null : SyntaxFactory.SimpleAsClause(baseType) ), SyntaxFactory.List(members) @@ -284,14 +284,14 @@ public override VisualBasicSyntaxNode VisitEnumMemberDeclaration(CSS.EnumMemberD { var initializer = (ExpressionSyntax)node.EqualsValue?.Value.Accept(TriviaConvertingVisitor); return SyntaxFactory.EnumMemberDeclaration( - SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))), CommonConversions.ConvertIdentifier(node.Identifier), + SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))), _commonConversions.ConvertIdentifier(node.Identifier), initializer == null ? null : SyntaxFactory.EqualsValue(initializer) ); } public override VisualBasicSyntaxNode VisitDelegateDeclaration(CSS.DelegateDeclarationSyntax node) { - var id = CommonConversions.ConvertIdentifier(node.Identifier); + var id = _commonConversions.ConvertIdentifier(node.Identifier); var methodInfo = ModelExtensions.GetDeclaredSymbol(_semanticModel, node) as INamedTypeSymbol; if (methodInfo.DelegateInvokeMethod.GetReturnType()?.SpecialType == SpecialType.System_Void) { return SyntaxFactory.DelegateSubStatement( @@ -360,7 +360,7 @@ public override VisualBasicSyntaxNode VisitDeclarationExpression(CSS.Declaration public override VisualBasicSyntaxNode VisitSingleVariableDesignation(CSS.SingleVariableDesignationSyntax node) { - return SyntaxFactory.IdentifierName(CommonConversions.ConvertIdentifier(node.Identifier)); + return SyntaxFactory.IdentifierName(_commonConversions.ConvertIdentifier(node.Identifier)); } public override VisualBasicSyntaxNode VisitDiscardDesignation(CSS.DiscardDesignationSyntax node) @@ -411,7 +411,7 @@ public override VisualBasicSyntaxNode VisitMethodDeclaration(CSS.MethodDeclarati if (node.Modifiers.Any(m => SyntaxTokenExtensions.IsKind(m, CS.SyntaxKind.ExternKeyword))) { block = SyntaxFactory.List(); } - var id = CommonConversions.ConvertIdentifier(node.Identifier); + var id = _commonConversions.ConvertIdentifier(node.Identifier); var methodInfo = ModelExtensions.GetDeclaredSymbol(_semanticModel, node); var containingType = methodInfo?.ContainingType; var attributes = SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))); @@ -472,7 +472,7 @@ private ImplementsClauseSyntax CreateImplementsClauseSyntaxOrNull(ISymbol member public override VisualBasicSyntaxNode VisitPropertyDeclaration(CSS.PropertyDeclarationSyntax node) { - var id = CommonConversions.ConvertIdentifier(node.Identifier); + var id = _commonConversions.ConvertIdentifier(node.Identifier); var modifiers = CommonConversions.ConvertModifiers(node.Modifiers, GetMemberContext(node)); var initializer = node.Initializer == null ? null : SyntaxFactory.EqualsValue((ExpressionSyntax)_commonConversions.ConvertTopLevelExpression(node.Initializer.Value)); @@ -568,7 +568,7 @@ public override VisualBasicSyntaxNode VisitEventDeclaration(CSS.EventDeclaration { ConvertAndSplitAttributes(node.AttributeLists, out SyntaxList attributes, out SyntaxList returnAttributes); var stmt = SyntaxFactory.EventStatement( - attributes, CommonConversions.ConvertModifiers(node.Modifiers, GetMemberContext(node)), CommonConversions.ConvertIdentifier(node.Identifier), null, + attributes, CommonConversions.ConvertModifiers(node.Modifiers, GetMemberContext(node)), _commonConversions.ConvertIdentifier(node.Identifier), null, SyntaxFactory.SimpleAsClause(returnAttributes, (TypeSyntax)node.Type.Accept(TriviaConvertingVisitor)), CreateImplementsClauseSyntaxOrNull(_semanticModel.GetDeclaredSymbol(node)) ); @@ -704,7 +704,7 @@ public override VisualBasicSyntaxNode VisitParenthesizedVariableDesignation(CSS. public override VisualBasicSyntaxNode VisitParameter(CSS.ParameterSyntax node) { - var id = CommonConversions.ConvertIdentifier(node.Identifier); + var id = _commonConversions.ConvertIdentifier(node.Identifier); var returnType = (TypeSyntax)node.Type?.Accept(TriviaConvertingVisitor); EqualsValueSyntax @default = null; if (node.Default != null) { @@ -1241,7 +1241,7 @@ public override VisualBasicSyntaxNode VisitQueryExpression(CSS.QueryExpressionSy public override VisualBasicSyntaxNode VisitFromClause(CSS.FromClauseSyntax node) { return SyntaxFactory.FromClause( - SyntaxFactory.CollectionRangeVariable(SyntaxFactory.ModifiedIdentifier(CommonConversions.ConvertIdentifier(node.Identifier)), + SyntaxFactory.CollectionRangeVariable(SyntaxFactory.ModifiedIdentifier(_commonConversions.ConvertIdentifier(node.Identifier)), (ExpressionSyntax)node.Expression.Accept(TriviaConvertingVisitor)) ); } @@ -1267,7 +1267,7 @@ IEnumerable ConvertQueryBody(CSS.QueryBodySyntax body) } else { var group = (CSS.GroupClauseSyntax)body.SelectOrGroup; var newGroupKeyName = GeneratePlaceholder("groupByKey"); - var csGroupId = CommonConversions.ConvertIdentifier(body.Continuation.Identifier); + var csGroupId = _commonConversions.ConvertIdentifier(body.Continuation.Identifier); var groupIdEquals = SyntaxFactory.VariableNameEquals(SyntaxFactory.ModifiedIdentifier(csGroupId)); var aggregationRangeVariableSyntax = SyntaxFactory.AggregationRangeVariable(groupIdEquals, SyntaxFactory.GroupAggregation()); yield return SyntaxFactory.GroupByClause( @@ -1296,7 +1296,7 @@ public override VisualBasicSyntaxNode VisitLetClause(CSS.LetClauseSyntax node) return SyntaxFactory.LetClause( SyntaxFactory.SingletonSeparatedList( SyntaxFactory.ExpressionRangeVariable( - SyntaxFactory.VariableNameEquals(SyntaxFactory.ModifiedIdentifier(CommonConversions.ConvertIdentifier(node.Identifier))), + SyntaxFactory.VariableNameEquals(SyntaxFactory.ModifiedIdentifier(_commonConversions.ConvertIdentifier(node.Identifier))), (ExpressionSyntax)node.Expression.Accept(TriviaConvertingVisitor) ) ) @@ -1307,13 +1307,13 @@ public override VisualBasicSyntaxNode VisitJoinClause(CSS.JoinClauseSyntax node) { if (node.Into != null) { return SyntaxFactory.GroupJoinClause( - SyntaxFactory.SingletonSeparatedList(SyntaxFactory.CollectionRangeVariable(SyntaxFactory.ModifiedIdentifier(CommonConversions.ConvertIdentifier(node.Identifier)), node.Type == null ? null : SyntaxFactory.SimpleAsClause((TypeSyntax)node.Type.Accept(TriviaConvertingVisitor)), (ExpressionSyntax)node.InExpression.Accept(TriviaConvertingVisitor))), + SyntaxFactory.SingletonSeparatedList(SyntaxFactory.CollectionRangeVariable(SyntaxFactory.ModifiedIdentifier(_commonConversions.ConvertIdentifier(node.Identifier)), node.Type == null ? null : SyntaxFactory.SimpleAsClause((TypeSyntax)node.Type.Accept(TriviaConvertingVisitor)), (ExpressionSyntax)node.InExpression.Accept(TriviaConvertingVisitor))), SyntaxFactory.SingletonSeparatedList(SyntaxFactory.JoinCondition((ExpressionSyntax)node.LeftExpression.Accept(TriviaConvertingVisitor), (ExpressionSyntax)node.RightExpression.Accept(TriviaConvertingVisitor))), - SyntaxFactory.SingletonSeparatedList(SyntaxFactory.AggregationRangeVariable(SyntaxFactory.VariableNameEquals(SyntaxFactory.ModifiedIdentifier(CommonConversions.ConvertIdentifier(node.Into.Identifier))), SyntaxFactory.GroupAggregation())) + SyntaxFactory.SingletonSeparatedList(SyntaxFactory.AggregationRangeVariable(SyntaxFactory.VariableNameEquals(SyntaxFactory.ModifiedIdentifier(_commonConversions.ConvertIdentifier(node.Into.Identifier))), SyntaxFactory.GroupAggregation())) ); } else { return SyntaxFactory.SimpleJoinClause( - SyntaxFactory.SingletonSeparatedList(SyntaxFactory.CollectionRangeVariable(SyntaxFactory.ModifiedIdentifier(CommonConversions.ConvertIdentifier(node.Identifier)), node.Type == null ? null : SyntaxFactory.SimpleAsClause((TypeSyntax)node.Type.Accept(TriviaConvertingVisitor)), (ExpressionSyntax)node.InExpression.Accept(TriviaConvertingVisitor))), + SyntaxFactory.SingletonSeparatedList(SyntaxFactory.CollectionRangeVariable(SyntaxFactory.ModifiedIdentifier(_commonConversions.ConvertIdentifier(node.Identifier)), node.Type == null ? null : SyntaxFactory.SimpleAsClause((TypeSyntax)node.Type.Accept(TriviaConvertingVisitor)), (ExpressionSyntax)node.InExpression.Accept(TriviaConvertingVisitor))), SyntaxFactory.SingletonSeparatedList(SyntaxFactory.JoinCondition((ExpressionSyntax)node.LeftExpression.Accept(TriviaConvertingVisitor), (ExpressionSyntax)node.RightExpression.Accept(TriviaConvertingVisitor))) ); } @@ -1398,7 +1398,7 @@ public override VisualBasicSyntaxNode VisitTypeParameter(CSS.TypeParameterSyntax } // copy generic constraints var clause = FindClauseForParameter(node); - return SyntaxFactory.TypeParameter(variance, CommonConversions.ConvertIdentifier(node.Identifier), (TypeParameterConstraintClauseSyntax)clause?.Accept(TriviaConvertingVisitor)); + return SyntaxFactory.TypeParameter(variance, _commonConversions.ConvertIdentifier(node.Identifier), (TypeParameterConstraintClauseSyntax)clause?.Accept(TriviaConvertingVisitor)); } public override VisualBasicSyntaxNode VisitTypeParameterConstraintClause(CSS.TypeParameterConstraintClauseSyntax node) @@ -1461,12 +1461,12 @@ public override VisualBasicSyntaxNode VisitOmittedTypeArgument(CSS.OmittedTypeAr public override VisualBasicSyntaxNode VisitIdentifierName(CSS.IdentifierNameSyntax node) { - return WrapTypedNameIfNecessary(SyntaxFactory.IdentifierName(CommonConversions.ConvertIdentifier(node.Identifier)), node); + return WrapTypedNameIfNecessary(SyntaxFactory.IdentifierName(_commonConversions.ConvertIdentifier(node.Identifier)), node); } public override VisualBasicSyntaxNode VisitGenericName(CSS.GenericNameSyntax node) { - return WrapTypedNameIfNecessary(SyntaxFactory.GenericName(CommonConversions.ConvertIdentifier(node.Identifier), (TypeArgumentListSyntax)node.TypeArgumentList.Accept(TriviaConvertingVisitor)), node); + return WrapTypedNameIfNecessary(SyntaxFactory.GenericName(_commonConversions.ConvertIdentifier(node.Identifier), (TypeArgumentListSyntax)node.TypeArgumentList.Accept(TriviaConvertingVisitor)), node); } public override VisualBasicSyntaxNode VisitQualifiedName(CSS.QualifiedNameSyntax node) diff --git a/Tests/TestRunners/ConverterTestBase.cs b/Tests/TestRunners/ConverterTestBase.cs index 9955e1ed3..7a1ae0776 100644 --- a/Tests/TestRunners/ConverterTestBase.cs +++ b/Tests/TestRunners/ConverterTestBase.cs @@ -28,7 +28,7 @@ public ConverterTestBase(string rootNamespace = null) return convertedCode; } - public void TestConversionCSharpToVisualBasic(string csharpCode, string expectedVisualBasicCode, bool expectSurroundingMethodBlock = false) + public void TestConversionCSharpToVisualBasic(string csharpCode, string expectedVisualBasicCode, bool expectSurroundingMethodBlock = false, bool expectCompilationErrors = false) { expectedVisualBasicCode = AddSurroundingMethodBlock(expectedVisualBasicCode, expectSurroundingMethodBlock); diff --git a/Tests/VB/SpecialConversionTests.cs b/Tests/VB/SpecialConversionTests.cs index 290a034d4..0f9b20064 100644 --- a/Tests/VB/SpecialConversionTests.cs +++ b/Tests/VB/SpecialConversionTests.cs @@ -53,10 +53,45 @@ End Sub } [Fact] - public void RaiseEvent() + public void RaiseEventInElse() { TestConversionCSharpToVisualBasic( - @"class TestClass + @"using System; + +public class Foo +{ + public event EventHandler Bar; + + protected void OnBar(EventArgs e) + { + if (Bar == null) + System.Diagnostics.Debug.WriteLine(""No subscriber""); + else + Bar.Invoke(this, e); + } +}", @"Imports System + +Public Class Foo + Public Event Bar As EventHandler(Of EventArgs) + + Protected Sub OnBar(ByVal e As EventArgs) + If BarEvent Is Nothing Then + System.Diagnostics.Debug.WriteLine(""No subscriber"") + Else + RaiseEvent Bar(Me, e) + End If + End Sub +End Class +"); + } + + [Fact] + public void RaiseEventMinimal() + { + TestConversionCSharpToVisualBasic( + @"using System; + +class TestClass { event EventHandler MyEvent; @@ -64,75 +99,141 @@ void TestMethod() { if (MyEvent != null) MyEvent(this, EventArgs.Empty); } -}", @"Friend Class TestClass +}", @"Imports System + +Friend Class TestClass Private Event MyEvent As EventHandler Private Sub TestMethod() RaiseEvent MyEvent(Me, EventArgs.Empty) End Sub End Class"); + } + + [Fact] + public void CharacterizeRaiseEventWithMissingDefinitionActsLikeFunc() + { TestConversionCSharpToVisualBasic( - @"class TestClass + @"using System; + +class TestClass { void TestMethod() { - if ((MyEvent != null)) MyEvent(this, EventArgs.Empty); + if (MyEvent != null) MyEvent(this, EventArgs.Empty); } -}", @"Friend Class TestClass +}", @"Imports System + +Friend Class TestClass Private Sub TestMethod() - RaiseEvent MyEvent(Me, EventArgs.Empty) + If MyEvent IsNot Nothing Then MyEvent(Me, EventArgs.Empty) End Sub End Class"); + } + + [Fact] + public void RaiseEventReversedConditional() + { TestConversionCSharpToVisualBasic( - @"class TestClass + @"using System; + +class TestClass { + event EventHandler MyEvent; + void TestMethod() { if (null != MyEvent) { MyEvent(this, EventArgs.Empty); } } -}", @"Friend Class TestClass +}", @"Imports System + +Friend Class TestClass + Private Event MyEvent As EventHandler + Private Sub TestMethod() RaiseEvent MyEvent(Me, EventArgs.Empty) End Sub End Class"); + } + + [Fact] + public void RaiseEventQualified() + { TestConversionCSharpToVisualBasic( - @"class TestClass + @"using System; + +class TestClass { + event EventHandler MyEvent; + void TestMethod() { if (this.MyEvent != null) MyEvent(this, EventArgs.Empty); } -}", @"Friend Class TestClass +}", @"Imports System + +Friend Class TestClass + Private Event MyEvent As EventHandler + Private Sub TestMethod() RaiseEvent MyEvent(Me, EventArgs.Empty) End Sub End Class"); + } + + [Fact] + public void RaiseEventInNestedBrackets() + { TestConversionCSharpToVisualBasic( - @"class TestClass + @"using System; + +class TestClass { + event EventHandler MyEvent; + void TestMethod() { - if (MyEvent != null) this.MyEvent(this, EventArgs.Empty); + if ((MyEvent != null)) this.MyEvent(this, EventArgs.Empty); } -}", @"Friend Class TestClass +}", @"Imports System + +Friend Class TestClass + Private Event MyEvent As EventHandler + Private Sub TestMethod() RaiseEvent MyEvent(Me, EventArgs.Empty) End Sub End Class"); + } + + [Fact] + public void RaiseEventQualifiedWithNestedBrackets() + { TestConversionCSharpToVisualBasic( - @"class TestClass + @"using System; + +class TestClass { + event EventHandler MyEvent; + void TestMethod() { if ((this.MyEvent != null)) { this.MyEvent(this, EventArgs.Empty); } } -}", @"Friend Class TestClass +}", @"Imports System + +Friend Class TestClass + Private Event MyEvent As EventHandler + Private Sub TestMethod() RaiseEvent MyEvent(Me, EventArgs.Empty) End Sub End Class"); } + /// + /// Intentionally unknown type used to ensure imperfect compilation errs towards common case + /// [Fact] public void IfStatementSimilarToRaiseEvent() { @@ -147,7 +248,7 @@ void TestMethod() Private Sub TestMethod() If FullImage IsNot Nothing Then DrawImage() End Sub -End Class"); +End Class", expectCompilationErrors: true); // regression test: TestConversionCSharpToVisualBasic( @"class TestClass @@ -160,7 +261,7 @@ void TestMethod() Private Sub TestMethod() If FullImage IsNot Nothing Then e.DrawImage() End Sub -End Class"); +End Class", expectCompilationErrors: true); // with braces: TestConversionCSharpToVisualBasic( @"class TestClass @@ -175,7 +276,7 @@ If FullImage IsNot Nothing Then DrawImage() End If End Sub -End Class"); +End Class", expectCompilationErrors: true); TestConversionCSharpToVisualBasic( @"class TestClass { @@ -189,7 +290,7 @@ If FullImage IsNot Nothing Then e.DrawImage() End If End Sub -End Class"); +End Class", expectCompilationErrors: true); // another bug related to the IfStatement code: TestConversionCSharpToVisualBasic( @"class TestClass @@ -207,7 +308,7 @@ For Each t As Tile In Tiles Next End If End Sub -End Class"); +End Class", expectCompilationErrors: true); } /// From 47e9a95d1cd0acf4c1166b2c102b75d999e0e0c1 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Sat, 16 Mar 2019 21:36:46 +0000 Subject: [PATCH 4/8] Always add Event suffix if it's an event. Now need to do special case for RaiseEvent Blah and RaiseEvent Me.Blah --- ICSharpCode.CodeConverter/VB/CommonConversions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.CodeConverter/VB/CommonConversions.cs b/ICSharpCode.CodeConverter/VB/CommonConversions.cs index 979b413fd..528791916 100644 --- a/ICSharpCode.CodeConverter/VB/CommonConversions.cs +++ b/ICSharpCode.CodeConverter/VB/CommonConversions.cs @@ -403,7 +403,7 @@ private ModifiedIdentifierSyntax ExtractIdentifier(Microsoft.CodeAnalysis.CSharp public SyntaxToken ConvertIdentifier(SyntaxToken id) { - var idText = id.ValueText; + var idText = IsEventHandlerType((CSharpSyntaxNode) id.Parent) ? id.ValueText + "Event" : id.ValueText; // Underscore is a special character in VB lexer which continues lines - not sure where to find the whole set of other similar tokens if any // Rather than a complicated contextual rename, just add an extra dash to all identifiers and hope this method is consistently used if (idText.All(c => c == '_')) idText += "_"; From a52846e0bdbd58b61c367be5d2a4fd5481288c6c Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Sun, 17 Mar 2019 13:17:25 +0000 Subject: [PATCH 5/8] Refine when to add Event suffix --- .../Shared/ProjectConversion.cs | 1 - .../VB/CommonConversions.cs | 38 ++++++++++++++--- .../VB/MethodBodyVisitor.cs | 42 +++++++++++-------- ICSharpCode.CodeConverter/VB/NodesVisitor.cs | 9 ++-- 4 files changed, 61 insertions(+), 29 deletions(-) diff --git a/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs b/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs index f0c7fb923..e04addd98 100644 --- a/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs +++ b/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs @@ -19,7 +19,6 @@ public class ProjectConversion { private readonly Compilation _sourceCompilation; private readonly IEnumerable _syntaxTreesToConvert; - // ReSharper disable once StaticMemberInGenericType - Stateless private static readonly AdhocWorkspace AdhocWorkspace = new AdhocWorkspace(); private readonly ConcurrentDictionary _errors = new ConcurrentDictionary(); private readonly Dictionary _firstPassResults = new Dictionary(); diff --git a/ICSharpCode.CodeConverter/VB/CommonConversions.cs b/ICSharpCode.CodeConverter/VB/CommonConversions.cs index 528791916..c635146d9 100644 --- a/ICSharpCode.CodeConverter/VB/CommonConversions.cs +++ b/ICSharpCode.CodeConverter/VB/CommonConversions.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Syntax; using AttributeListSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.AttributeListSyntax; +using BinaryExpressionSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.BinaryExpressionSyntax; using CSharpExtensions = Microsoft.CodeAnalysis.CSharp.CSharpExtensions; using CSSyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind; using ExpressionSyntax = Microsoft.CodeAnalysis.VisualBasic.Syntax.ExpressionSyntax; @@ -403,12 +404,18 @@ private ModifiedIdentifierSyntax ExtractIdentifier(Microsoft.CodeAnalysis.CSharp public SyntaxToken ConvertIdentifier(SyntaxToken id) { - var idText = IsEventHandlerType((CSharpSyntaxNode) id.Parent) ? id.ValueText + "Event" : id.ValueText; + CSharpSyntaxNode parent = (CSharpSyntaxNode) id.Parent; + var idText = IsEventHandlerIdentifier(parent) ? id.ValueText + "Event" : id.ValueText; // Underscore is a special character in VB lexer which continues lines - not sure where to find the whole set of other similar tokens if any // Rather than a complicated contextual rename, just add an extra dash to all identifiers and hope this method is consistently used + bool keywordRequiresEscaping = KeywordRequiresEscaping(id); + return Identifier(idText, keywordRequiresEscaping); + } + + public static SyntaxToken Identifier(string idText, bool keywordRequiresEscaping = false) + { if (idText.All(c => c == '_')) idText += "_"; - - return KeywordRequiresEscaping(id) ? SyntaxFactory.Identifier($"[{idText}]") : SyntaxFactory.Identifier(idText); + return keywordRequiresEscaping ? SyntaxFactory.Identifier($"[{idText}]") : SyntaxFactory.Identifier(idText); } private static bool KeywordRequiresEscaping(SyntaxToken id) @@ -531,9 +538,30 @@ private static string UppercaseFirstLetter(string sourceText) return sourceText.Substring(0, 1).ToUpper() + sourceText.Substring(1); } - public bool IsEventHandlerType(CSharpSyntaxNode syntax) + public bool IsEventHandlerIdentifier(CSharpSyntaxNode syntax) + { + return GetSymbol(syntax).IsKind(SymbolKind.Event) && !IsEventHandlerAssignLhs(syntax); + } + + private static bool IsEventHandlerAssignLhs(CSharpSyntaxNode syntax) + { + var assignmentExpressionSyntax = syntax.GetAncestor(); + return assignmentExpressionSyntax != null && assignmentExpressionSyntax.IsKind(CSSyntaxKind.AddAssignmentExpression, CSSyntaxKind.SubtractAssignmentExpression) + && assignmentExpressionSyntax.Left.DescendantNodes().Contains(syntax) == true; + } + + private ISymbol GetSymbol(CSharpSyntaxNode syntax) + { + return syntax.SyntaxTree == _semanticModel.SyntaxTree + ? _semanticModel.GetSymbolInfo(syntax).Symbol + : null; + } + + private ITypeSymbol GetTypeSymbol(CSharpSyntaxNode syntax) { - return _semanticModel.GetTypeInfo(syntax).Type?.GetFullMetadataName()?.StartsWith("System.EventHandler") == true; + return syntax.SyntaxTree == _semanticModel.SyntaxTree + ? _semanticModel.GetTypeInfo(syntax).Type + : null; } } } \ No newline at end of file diff --git a/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs b/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs index d14b91a6c..6ad952970 100644 --- a/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs @@ -41,12 +41,6 @@ class BlockInfo } public CommentConvertingMethodBodyVisitor CommentConvertingVisitor { get; } - public SemanticModel SemanticModel - { - set { _semanticModel = value; } - get { return _semanticModel; } - } - public MethodBodyVisitor(SemanticModel semanticModel, CSharpSyntaxVisitor nodesVisitor, TriviaConverter triviaConverter, CommonConversions commonConversions) @@ -187,20 +181,27 @@ private bool TryConvertRaiseEvent(CSS.StatementSyntax resultStatement, if (singleStatement == null || !(singleStatement.Expression is CSS.InvocationExpressionSyntax)) return false; - var eventIdentifier = _commonConversions.IsEventHandlerType(be.Left) ? be.Left - : _commonConversions.IsEventHandlerType(be.Right) ? be.Right + var eventIdentifier = _commonConversions.IsEventHandlerIdentifier(be.Left) ? be.Left + : _commonConversions.IsEventHandlerIdentifier(be.Right) ? be.Right : null; if (eventIdentifier == null) return false; - var nameExpr = eventIdentifier.Accept(_nodesVisitor); - var nameIdentifier = nameExpr is MemberAccessExpressionSyntax maes ? maes.Name : nameExpr; + var handlerName = ConvertEventHandlerName(eventIdentifier); var invocation = (CSS.InvocationExpressionSyntax) singleStatement.Expression; var arguments = invocation.ArgumentList.Arguments.Select(a => (ArgumentSyntax) a.Accept(_nodesVisitor)); - raiseEventStatement = SyntaxFactory.RaiseEventStatement((IdentifierNameSyntax) nameIdentifier -, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))); + raiseEventStatement = SyntaxFactory.RaiseEventStatement(handlerName, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))); return true; } + private IdentifierNameSyntax ConvertEventHandlerName(CSS.ExpressionSyntax eventIdentifier) + { + var nameExpr = eventIdentifier.Accept(_nodesVisitor); + var eventName = (SimpleNameSyntax) (nameExpr is MemberAccessExpressionSyntax maes ? maes.Name : nameExpr); + string identifierText = eventName.Identifier.Text; + if (identifierText.EndsWith("Event")) identifierText = identifierText.Substring(0, identifierText.Length - "Event".Length); + return SyntaxFactory.IdentifierName(identifierText); + } + void CollectElseBlocks(CSS.IfStatementSyntax node, List elseIfBlocks, ref ElseBlockSyntax elseBlock) { if (node.Else == null) return; @@ -589,12 +590,17 @@ SyntaxList ConvertBlock(CSS.StatementSyntax node, params Statem public override SyntaxList VisitReturnStatement(CSS.ReturnStatementSyntax node) { - StatementSyntax stmt; - if (node.Expression == null) - stmt = SyntaxFactory.ReturnStatement(); - else - stmt = SyntaxFactory.ReturnStatement((ExpressionSyntax)node.Expression.Accept(_nodesVisitor)); - return SyntaxFactory.SingletonList(stmt); + var vbExpression = node.Expression?.Accept(_nodesVisitor); + return SyntaxFactory.SingletonList(ReturnStatement(vbExpression)); + } + + private static StatementSyntax ReturnStatement(VisualBasicSyntaxNode vbExpression) + { + return vbExpression == null + ? SyntaxFactory.ReturnStatement() + : vbExpression.IsKind(SyntaxKind.EmptyStatement) + ? SyntaxFactory.ReturnStatement().WithTriviaFrom(vbExpression) + : SyntaxFactory.ReturnStatement((ExpressionSyntax)vbExpression); } public override SyntaxList VisitYieldStatement(CSS.YieldStatementSyntax node) diff --git a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs index 9f47dff63..f011039bb 100644 --- a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs @@ -1522,11 +1522,10 @@ private TypeSyntax GetOverloadedFormalParameterTypeOrNull(CSS.ExpressionSyntax a var symbolInfo = _semanticModel.GetSymbolInfo(ies.Expression); var destinationType = symbolInfo.ExtractBestMatch(m => m.GetParameters().Length > argIndex); if (destinationType != null) { - var toCreate = (TypeSyntax) - CS.SyntaxFactory - .ParseTypeName(destinationType.GetParameters()[argIndex].Type.ToMinimalDisplayString(_semanticModel, - argumentChildExpression.SpanStart)) - .Accept(TriviaConvertingVisitor); + string symbolName = destinationType.GetParameters()[argIndex].Type + .ToMinimalDisplayString(_semanticModel, argumentChildExpression.SpanStart); + var csType = CS.SyntaxFactory.ParseTypeName(symbolName); + var toCreate = (TypeSyntax) csType.Accept(TriviaConvertingVisitor); return toCreate; } } From b58452cb09c9ff2c9ca7081561936cef72ed2d3f Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Sun, 17 Mar 2019 16:47:11 +0000 Subject: [PATCH 6/8] Minor tidyups --- .../Shared/ProjectConversion.cs | 15 ++++++++------- ICSharpCode.CodeConverter/VB/CommonConversions.cs | 5 +++-- ICSharpCode.CodeConverter/VB/NodesVisitor.cs | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs b/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs index e04addd98..32700d319 100644 --- a/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs +++ b/ICSharpCode.CodeConverter/Shared/ProjectConversion.cs @@ -19,7 +19,6 @@ public class ProjectConversion { private readonly Compilation _sourceCompilation; private readonly IEnumerable _syntaxTreesToConvert; - private static readonly AdhocWorkspace AdhocWorkspace = new AdhocWorkspace(); private readonly ConcurrentDictionary _errors = new ConcurrentDictionary(); private readonly Dictionary _firstPassResults = new Dictionary(); private readonly ILanguageConversion _languageConversion; @@ -146,12 +145,13 @@ private Dictionary Convert() private Dictionary SecondPass() { var secondPassByFilePath = new Dictionary(); + var adhocWorkspace = new AdhocWorkspace(); foreach (var firstPassResult in _firstPassResults) { var treeFilePath = firstPassResult.Key; try { - secondPassByFilePath.Add(treeFilePath, SingleSecondPass(firstPassResult)); + secondPassByFilePath.Add(treeFilePath, SingleSecondPass(firstPassResult, adhocWorkspace)); } catch (Exception e) { - secondPassByFilePath.Add(treeFilePath, Format(firstPassResult.Value.GetRoot())); + secondPassByFilePath.Add(treeFilePath, Format(firstPassResult.Value.GetRoot(), adhocWorkspace)); _errors.TryAdd(treeFilePath, e.ToString()); } } @@ -168,10 +168,10 @@ private void AddProjectWarnings() } } - private SyntaxNode SingleSecondPass(KeyValuePair cs) + private SyntaxNode SingleSecondPass(KeyValuePair cs, AdhocWorkspace workspace) { var secondPassNode = _languageConversion.SingleSecondPass(cs); - return Format(secondPassNode); + return Format(secondPassNode, workspace); } private void FirstPass() @@ -236,10 +236,11 @@ private static async Task GetSyntaxTreeWithAnnotatedSelection(Syntax return root.WithAnnotatedNode(selectedNode, AnnotationConstants.SelectedNodeAnnotationKind); } - private SyntaxNode Format(SyntaxNode resultNode) + private SyntaxNode Format(SyntaxNode resultNode, Workspace workspace) { SyntaxNode selectedNode = _handlePartialConversion ? GetSelectedNode(resultNode) : resultNode; - return Formatter.Format(selectedNode ?? resultNode, AdhocWorkspace); + SyntaxNode nodeToFormat = selectedNode ?? resultNode; + return Formatter.Format(nodeToFormat, workspace); } private SyntaxNode GetSelectedNode(SyntaxNode resultNode) diff --git a/ICSharpCode.CodeConverter/VB/CommonConversions.cs b/ICSharpCode.CodeConverter/VB/CommonConversions.cs index c635146d9..90770cb2f 100644 --- a/ICSharpCode.CodeConverter/VB/CommonConversions.cs +++ b/ICSharpCode.CodeConverter/VB/CommonConversions.cs @@ -207,7 +207,7 @@ public ExpressionSyntax ReduceArrayUpperBoundExpression(Microsoft.CodeAnalysis.C public LambdaExpressionSyntax ConvertLambdaExpression(AnonymousFunctionExpressionSyntax node, CSharpSyntaxNode body, IEnumerable parameters, SyntaxTokenList modifiers) { - var symbol = ModelExtensions.GetSymbolInfo(_semanticModel, node).Symbol as IMethodSymbol; + var symbol = (IMethodSymbol) ModelExtensions.GetSymbolInfo(_semanticModel, node).Symbol; var parameterList = SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters.Select(p => (Microsoft.CodeAnalysis.VisualBasic.Syntax.ParameterSyntax)p.Accept(_nodesVisitor)))); LambdaHeaderSyntax header; EndBlockStatementSyntax endBlock; @@ -234,7 +234,8 @@ public LambdaExpressionSyntax ConvertLambdaExpression(AnonymousFunctionExpressio var vbThrowExpression = (ExpressionSyntax)csThrowExpression.Expression.Accept(_nodesVisitor); var vbThrowStatement = SyntaxFactory.ThrowStatement(SyntaxFactory.Token(SyntaxKind.ThrowKeyword), vbThrowExpression); - return SyntaxFactory.MultiLineFunctionLambdaExpression(header, SyntaxFactory.SingletonList(vbThrowStatement), endBlock); + return SyntaxFactory.MultiLineFunctionLambdaExpression(header, + SyntaxFactory.SingletonList(vbThrowStatement), endBlock); } else { statements = InsertRequiredDeclarations( SyntaxFactory.SingletonList( diff --git a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs index f011039bb..13298e69d 100644 --- a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs @@ -292,7 +292,7 @@ public override VisualBasicSyntaxNode VisitEnumMemberDeclaration(CSS.EnumMemberD public override VisualBasicSyntaxNode VisitDelegateDeclaration(CSS.DelegateDeclarationSyntax node) { var id = _commonConversions.ConvertIdentifier(node.Identifier); - var methodInfo = ModelExtensions.GetDeclaredSymbol(_semanticModel, node) as INamedTypeSymbol; + var methodInfo = (INamedTypeSymbol) _semanticModel.GetDeclaredSymbol(node); if (methodInfo.DelegateInvokeMethod.GetReturnType()?.SpecialType == SpecialType.System_Void) { return SyntaxFactory.DelegateSubStatement( SyntaxFactory.List(node.AttributeLists.Select(a => (AttributeListSyntax)a.Accept(TriviaConvertingVisitor))), CommonConversions.ConvertModifiers(node.Modifiers), From afecf054993d07d1d11f9f8b8453bb967d361026 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Sun, 17 Mar 2019 18:45:52 +0000 Subject: [PATCH 7/8] Work for plain invocations --- CHANGELOG.md | 7 +- .../VB/CommonConversions.cs | 6 +- .../VB/MethodBodyVisitor.cs | 37 +-------- ICSharpCode.CodeConverter/VB/NodesVisitor.cs | 41 +++++++++- Tests/VB/SpecialConversionTests.cs | 78 ++++++++++--------- 5 files changed, 92 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 785c9f422..d3c01c243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ # Change Log All notable changes to the code converter will be documented here. -# 6.5.0 TBC +# 6.6.0 TBC + +### C# -> VB +* Improve event identifier conversion + +# 6.5.0 03/03/2019 * Avoid fatal error converting a project in a solution containing a website project (#243) * Improve best-effort conversion in the presence of errors * Improved nuget package and web converter's snippet detection diff --git a/ICSharpCode.CodeConverter/VB/CommonConversions.cs b/ICSharpCode.CodeConverter/VB/CommonConversions.cs index 90770cb2f..1d9bba88a 100644 --- a/ICSharpCode.CodeConverter/VB/CommonConversions.cs +++ b/ICSharpCode.CodeConverter/VB/CommonConversions.cs @@ -406,7 +406,7 @@ private ModifiedIdentifierSyntax ExtractIdentifier(Microsoft.CodeAnalysis.CSharp public SyntaxToken ConvertIdentifier(SyntaxToken id) { CSharpSyntaxNode parent = (CSharpSyntaxNode) id.Parent; - var idText = IsEventHandlerIdentifier(parent) ? id.ValueText + "Event" : id.ValueText; + var idText = IsEventHandlerIdentifier(parent) && !IsEventHandlerAssignLhs(parent) ? id.ValueText + "Event" : id.ValueText; // Underscore is a special character in VB lexer which continues lines - not sure where to find the whole set of other similar tokens if any // Rather than a complicated contextual rename, just add an extra dash to all identifiers and hope this method is consistently used bool keywordRequiresEscaping = KeywordRequiresEscaping(id); @@ -541,14 +541,14 @@ private static string UppercaseFirstLetter(string sourceText) public bool IsEventHandlerIdentifier(CSharpSyntaxNode syntax) { - return GetSymbol(syntax).IsKind(SymbolKind.Event) && !IsEventHandlerAssignLhs(syntax); + return GetSymbol(syntax).IsKind(SymbolKind.Event); } private static bool IsEventHandlerAssignLhs(CSharpSyntaxNode syntax) { var assignmentExpressionSyntax = syntax.GetAncestor(); return assignmentExpressionSyntax != null && assignmentExpressionSyntax.IsKind(CSSyntaxKind.AddAssignmentExpression, CSSyntaxKind.SubtractAssignmentExpression) - && assignmentExpressionSyntax.Left.DescendantNodes().Contains(syntax) == true; + && assignmentExpressionSyntax.Left.DescendantNodes().Contains(syntax); } private ISymbol GetSymbol(CSharpSyntaxNode syntax) diff --git a/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs b/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs index 6ad952970..e659b0988 100644 --- a/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/MethodBodyVisitor.cs @@ -90,11 +90,7 @@ public override SyntaxList VisitIfStatement(CSS.IfStatementSynt } var elseIfBlocks = new List(); ElseBlockSyntax elseBlock = null; - if (TryConvertElseRaiseEvent(node, out var elseStmt)) { - elseBlock = SyntaxFactory.ElseBlock(SyntaxFactory.SingletonList(elseStmt)); - } else { - CollectElseBlocks(node, elseIfBlocks, ref elseBlock); - } + CollectElseBlocks(node, elseIfBlocks, ref elseBlock); if (node.Statement is CSS.BlockSyntax) { stmt = SyntaxFactory.MultiLineIfBlock( @@ -139,14 +135,6 @@ bool TryConvertIfNotNullRaiseEvent(CSS.IfStatementSyntax node, out StatementSynt && TryConvertRaiseEvent(node.Statement, comparisonExpression, ref raiseEventStatement); } - bool TryConvertElseRaiseEvent(CSS.IfStatementSyntax node, out StatementSyntax raiseEventStatement) - { - raiseEventStatement = null; - return node.Else != null && - TryGetBinaryExpression(node, out var comparisonExpression, CS.SyntaxKind.EqualsExpression, CS.SyntaxKind.NullLiteralExpression) - && TryConvertRaiseEvent(node.Else.Statement, comparisonExpression, ref raiseEventStatement); - } - private static bool TryGetBinaryExpression(CSS.IfStatementSyntax node, out CSS.BinaryExpressionSyntax binaryExpressionSyntax, CS.SyntaxKind notEqualsExpression, CS.SyntaxKind operand) { binaryExpressionSyntax = TrimParenthesis(node) as CSS.BinaryExpressionSyntax; @@ -178,28 +166,11 @@ private bool TryConvertRaiseEvent(CSS.StatementSyntax resultStatement, singleStatement = resultStatement as CSS.ExpressionStatementSyntax; } - if (singleStatement == null || !(singleStatement.Expression is CSS.InvocationExpressionSyntax)) + if (!(singleStatement?.Expression is CSS.InvocationExpressionSyntax singleInvocationExpression)) return false; - var eventIdentifier = _commonConversions.IsEventHandlerIdentifier(be.Left) ? be.Left - : _commonConversions.IsEventHandlerIdentifier(be.Right) ? be.Right - : null; - if (eventIdentifier == null) return false; - - var handlerName = ConvertEventHandlerName(eventIdentifier); - var invocation = (CSS.InvocationExpressionSyntax) singleStatement.Expression; - var arguments = invocation.ArgumentList.Arguments.Select(a => (ArgumentSyntax) a.Accept(_nodesVisitor)); - raiseEventStatement = SyntaxFactory.RaiseEventStatement(handlerName, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))); - return true; - } - - private IdentifierNameSyntax ConvertEventHandlerName(CSS.ExpressionSyntax eventIdentifier) - { - var nameExpr = eventIdentifier.Accept(_nodesVisitor); - var eventName = (SimpleNameSyntax) (nameExpr is MemberAccessExpressionSyntax maes ? maes.Name : nameExpr); - string identifierText = eventName.Identifier.Text; - if (identifierText.EndsWith("Event")) identifierText = identifierText.Substring(0, identifierText.Length - "Event".Length); - return SyntaxFactory.IdentifierName(identifierText); + raiseEventStatement = singleInvocationExpression.Accept(_nodesVisitor) as RaiseEventStatementSyntax; + return raiseEventStatement != null; } void CollectElseBlocks(CSS.IfStatementSyntax node, List elseIfBlocks, ref ElseBlockSyntax elseBlock) diff --git a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs index 13298e69d..11080661d 100644 --- a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs @@ -951,10 +951,43 @@ public override VisualBasicSyntaxNode VisitInvocationExpression(CSS.InvocationEx return SyntaxFactory.NameOfExpression(convertedExpression); } - return SyntaxFactory.InvocationExpression( - (ExpressionSyntax)node.Expression.Accept(TriviaConvertingVisitor), - (ArgumentListSyntax)node.ArgumentList.Accept(TriviaConvertingVisitor) - ); + var invokedCsExpression = node.Expression; + if (invokedCsExpression is CSS.MemberAccessExpressionSyntax csMemberAccess && csMemberAccess.Name.Identifier.Value.Equals("Invoke") && _commonConversions.IsEventHandlerIdentifier(csMemberAccess.Expression)) { + invokedCsExpression = csMemberAccess.Expression; + } + + var vbEventExpression = (ExpressionSyntax)invokedCsExpression.Accept(TriviaConvertingVisitor); + var argumentListSyntax = (ArgumentListSyntax)node.ArgumentList.Accept(TriviaConvertingVisitor); + + if (_commonConversions.IsEventHandlerIdentifier(invokedCsExpression)) { + return SyntaxFactory.RaiseEventStatement(RemoveEventSuffix(GetSimpleName(vbEventExpression)), + argumentListSyntax); + } + + return SyntaxFactory.InvocationExpression(vbEventExpression, argumentListSyntax); + } + + private IdentifierNameSyntax RemoveEventSuffix(SimpleNameSyntax name) + { + var identifierName = name.Identifier.ValueText; + if (identifierName.EndsWith("Event")) { + identifierName = identifierName.Substring(0, identifierName.Length - "Event".Length); + } + return SyntaxFactory.IdentifierName(identifierName); + } + + private SimpleNameSyntax GetSimpleName(ExpressionSyntax expressionSyntax) + { + switch (expressionSyntax) + { + case SimpleNameSyntax simpleName: + return simpleName; + case MemberAccessExpressionSyntax memberAccess: + return GetSimpleName(memberAccess.Name); + default: + throw new NotSupportedException( + $"Cannot get SimpleNameSyntax from {expressionSyntax.Kind()}:\r\n{expressionSyntax}"); + } } private bool IsNameOfExpression(CSS.InvocationExpressionSyntax node) diff --git a/Tests/VB/SpecialConversionTests.cs b/Tests/VB/SpecialConversionTests.cs index 0f9b20064..77f42b579 100644 --- a/Tests/VB/SpecialConversionTests.cs +++ b/Tests/VB/SpecialConversionTests.cs @@ -52,6 +52,37 @@ End Sub End Class"); } + [Fact] + public void RaiseEventOneLiners() + { + TestConversionCSharpToVisualBasic( + @"using System; + +class TestClass +{ + event EventHandler MyEvent; + + void TestMethod() + { + MyEvent(this, EventArgs.Empty); + if (MyEvent != null) MyEvent(this, EventArgs.Empty); + MyEvent.Invoke(this, EventArgs.Empty); + MyEvent?.Invoke(this, EventArgs.Empty); + } +}", @"Imports System + +Friend Class TestClass + Private Event MyEvent As EventHandler + + Private Sub TestMethod() + RaiseEvent MyEvent(Me, EventArgs.Empty) + RaiseEvent MyEvent(Me, EventArgs.Empty) + RaiseEvent MyEvent(Me, EventArgs.Empty) + RaiseEvent MyEvent(Me, EventArgs.Empty) + End Sub +End Class"); + } + [Fact] public void RaiseEventInElse() { @@ -86,7 +117,7 @@ End Class } [Fact] - public void RaiseEventMinimal() + public void RaiseEventReversedConditional() { TestConversionCSharpToVisualBasic( @"using System; @@ -97,7 +128,7 @@ class TestClass void TestMethod() { - if (MyEvent != null) MyEvent(this, EventArgs.Empty); + if (null != MyEvent) { MyEvent(this, EventArgs.Empty); } } }", @"Imports System @@ -111,28 +142,7 @@ End Sub } [Fact] - public void CharacterizeRaiseEventWithMissingDefinitionActsLikeFunc() - { - TestConversionCSharpToVisualBasic( - @"using System; - -class TestClass -{ - void TestMethod() - { - if (MyEvent != null) MyEvent(this, EventArgs.Empty); - } -}", @"Imports System - -Friend Class TestClass - Private Sub TestMethod() - If MyEvent IsNot Nothing Then MyEvent(Me, EventArgs.Empty) - End Sub -End Class"); - } - - [Fact] - public void RaiseEventReversedConditional() + public void RaiseEventQualified() { TestConversionCSharpToVisualBasic( @"using System; @@ -143,7 +153,7 @@ class TestClass void TestMethod() { - if (null != MyEvent) { MyEvent(this, EventArgs.Empty); } + if (this.MyEvent != null) this.MyEvent(this, EventArgs.Empty); } }", @"Imports System @@ -157,7 +167,7 @@ End Sub } [Fact] - public void RaiseEventQualified() + public void RaiseEventInNestedBrackets() { TestConversionCSharpToVisualBasic( @"using System; @@ -168,7 +178,7 @@ class TestClass void TestMethod() { - if (this.MyEvent != null) MyEvent(this, EventArgs.Empty); + if ((MyEvent != null)) this.MyEvent.Invoke(this, EventArgs.Empty); } }", @"Imports System @@ -182,7 +192,7 @@ End Sub } [Fact] - public void RaiseEventInNestedBrackets() + public void RaiseEventQualifiedWithNestedBrackets() { TestConversionCSharpToVisualBasic( @"using System; @@ -193,7 +203,7 @@ class TestClass void TestMethod() { - if ((MyEvent != null)) this.MyEvent(this, EventArgs.Empty); + if ((this.MyEvent != null)) { this.MyEvent(this, EventArgs.Empty); } } }", @"Imports System @@ -207,26 +217,22 @@ End Sub } [Fact] - public void RaiseEventQualifiedWithNestedBrackets() + public void CharacterizeRaiseEventWithMissingDefinitionActsLikeFunc() { TestConversionCSharpToVisualBasic( @"using System; class TestClass { - event EventHandler MyEvent; - void TestMethod() { - if ((this.MyEvent != null)) { this.MyEvent(this, EventArgs.Empty); } + if (MyEvent != null) MyEvent(this, EventArgs.Empty); } }", @"Imports System Friend Class TestClass - Private Event MyEvent As EventHandler - Private Sub TestMethod() - RaiseEvent MyEvent(Me, EventArgs.Empty) + If MyEvent IsNot Nothing Then MyEvent(Me, EventArgs.Empty) End Sub End Class"); } From 5c06d29183fca10a8b9195ad21ff643b04530b27 Mon Sep 17 00:00:00 2001 From: GrahamTheCoder Date: Sun, 17 Mar 2019 19:55:33 +0000 Subject: [PATCH 8/8] Convert conditional invocations --- ICSharpCode.CodeConverter/VB/NodesVisitor.cs | 33 +++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs index 11080661d..352b5d4c0 100644 --- a/ICSharpCode.CodeConverter/VB/NodesVisitor.cs +++ b/ICSharpCode.CodeConverter/VB/NodesVisitor.cs @@ -952,19 +952,33 @@ public override VisualBasicSyntaxNode VisitInvocationExpression(CSS.InvocationEx } var invokedCsExpression = node.Expression; - if (invokedCsExpression is CSS.MemberAccessExpressionSyntax csMemberAccess && csMemberAccess.Name.Identifier.Value.Equals("Invoke") && _commonConversions.IsEventHandlerIdentifier(csMemberAccess.Expression)) { + if (invokedCsExpression is CSS.MemberAccessExpressionSyntax csMemberAccess && IsInvokeIdentifier(csMemberAccess.Name)) { invokedCsExpression = csMemberAccess.Expression; } + if (TryCreateRaiseEventStatement(invokedCsExpression, node.ArgumentList, out VisualBasicSyntaxNode visitInvocationExpression)) { + return visitInvocationExpression; + } + var vbEventExpression = (ExpressionSyntax)invokedCsExpression.Accept(TriviaConvertingVisitor); var argumentListSyntax = (ArgumentListSyntax)node.ArgumentList.Accept(TriviaConvertingVisitor); + return SyntaxFactory.InvocationExpression(vbEventExpression, argumentListSyntax); + } - if (_commonConversions.IsEventHandlerIdentifier(invokedCsExpression)) { - return SyntaxFactory.RaiseEventStatement(RemoveEventSuffix(GetSimpleName(vbEventExpression)), - argumentListSyntax); + private bool TryCreateRaiseEventStatement(CSS.ExpressionSyntax invokedCsExpression, + CSS.ArgumentListSyntax argumentListSyntax, out VisualBasicSyntaxNode visitInvocationExpression) + { + if (_commonConversions.IsEventHandlerIdentifier(invokedCsExpression)) + { + var expressionSyntax = (ExpressionSyntax)invokedCsExpression.Accept(TriviaConvertingVisitor); + var identifierNameSyntax = RemoveEventSuffix(GetSimpleName(expressionSyntax)); + var argumentList = (ArgumentListSyntax)argumentListSyntax.Accept(TriviaConvertingVisitor); + visitInvocationExpression = SyntaxFactory.RaiseEventStatement(identifierNameSyntax, argumentList); + return true; } - return SyntaxFactory.InvocationExpression(vbEventExpression, argumentListSyntax); + visitInvocationExpression = null; + return false; } private IdentifierNameSyntax RemoveEventSuffix(SimpleNameSyntax name) @@ -1009,6 +1023,10 @@ public override VisualBasicSyntaxNode VisitConditionalExpression(CSS.Conditional public override VisualBasicSyntaxNode VisitConditionalAccessExpression(CSS.ConditionalAccessExpressionSyntax node) { + if (node.WhenNotNull is CSS.InvocationExpressionSyntax invocation && invocation.Expression is CSS.MemberBindingExpressionSyntax binding && IsInvokeIdentifier(binding.Name) && TryCreateRaiseEventStatement(node.Expression, invocation.ArgumentList, out var raiseEventStatement)) { + return raiseEventStatement; + } + return SyntaxFactory.ConditionalAccessExpression( (ExpressionSyntax)node.Expression.Accept(TriviaConvertingVisitor), SyntaxFactory.Token(SyntaxKind.QuestionToken), @@ -1016,6 +1034,11 @@ public override VisualBasicSyntaxNode VisitConditionalAccessExpression(CSS.Condi ); } + private static bool IsInvokeIdentifier(CSS.SimpleNameSyntax sns) + { + return sns.Identifier.Value.Equals("Invoke"); + } + public override VisualBasicSyntaxNode VisitMemberAccessExpression(CSS.MemberAccessExpressionSyntax node) { return WrapTypedNameIfNecessary(SyntaxFactory.MemberAccessExpression(