using System; using System.Linq; using CTA.Rules.Actions.ActionHelpers; using CTA.Rules.Config; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Syntax; namespace CTA.Rules.Actions.VisualBasic { /// /// List of actions that can run on Invocation Expressions /// public class InvocationExpressionActions { /// /// This Method replaces the entire expression including the parameters. For example, Math.Round(5.5) invocation expression matching on Round with a newMethod parameter of Abs(9.5) would return Abs(9.5) not Math.Abs(9.5). /// Please ensure to include the prefix part of the matching invocation expression since this method will replace it and the parameters as well. /// /// The new invocation expression including the method to replace with and the parameters. /// The new invocation expression parameters to replace the old parameters with. /// public Func GetReplaceMethodWithObjectAndParametersAction(string newMethod, string newParameters) { //TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parse expression ? InvocationExpressionSyntax ReplaceMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node) { node = SyntaxFactory.InvocationExpression( SyntaxFactory.IdentifierName(newMethod), SyntaxFactory.ParseArgumentList(newParameters)) .WithLeadingTrivia(node.GetLeadingTrivia()) .NormalizeWhitespace(); return node; } return ReplaceMethod; } /// /// This Method replaces the entire expression up to the matching invocation expression. For example, Math.Round(5.5) invocation expression matching on Round with a newMethod parameter of Abs would return Abs(5.5) not Math.Abs(5.5). /// Please ensure to include the prefix part of the matching invocation expression since this method will replace it. /// /// The new invocation expression including the method to replace with. /// public Func GetReplaceMethodWithObjectAction(string newMethod) { //TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parseexpression ? InvocationExpressionSyntax ReplaceMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node) { node = node.WithExpression(SyntaxFactory.ParseExpression(newMethod)) .WithLeadingTrivia(node.GetLeadingTrivia()) .NormalizeWhitespace(); return node; } return ReplaceMethod; } /// /// This Method replaces the entire expression up to the matching invocation expression. It also adds the type from the typed argument list to the list of arguments. /// Example: DependencyResolver.Current.GetService() becomes DependencyResolver.Current.GetService(typeof(object)) /// /// The new invocation expression including the method to replace with. /// public Func GetReplaceMethodWithObjectAddTypeAction(string newMethod) { InvocationExpressionSyntax ReplaceMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node) { var typeToAdd = node.Expression.DescendantNodes()?.OfType()?.FirstOrDefault()?.TypeArgumentList; if (typeToAdd != null) { var argumentList = SyntaxFactory.ParseArgumentList($"(TypeOf {typeToAdd.Arguments.ToString()})"); node = node.WithArgumentList(argumentList).WithLeadingTrivia(node.GetLeadingTrivia()); } // in the test, the NormalizeWhitespace() was added weird white spaces .GetService(TypeOf Object )" node = node.WithExpression(SyntaxFactory.ParseExpression(newMethod)).WithLeadingTrivia(node.GetLeadingTrivia()); return node; } return ReplaceMethod; } /// /// This Method replaces only matching method in the invocation expression and its parameters. /// For example, Math.Round(5.5) invocation expression matching on Round with a newMethod parameter of Abs and an oldMethod parameter of Round with parameters of 8.5 would return Math.Abs(8.5). /// /// The matching method in the invocation expression to be replaced. /// The new method to replace the old method with in the invocation expression. /// The new invocation expression parameters to replace the old parameters with. /// public Func GetReplaceMethodAndParametersAction(string oldMethod, string newMethod, string newParameters) { //TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parseexpression ? InvocationExpressionSyntax ReplaceOnlyMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node) { node = node.WithExpression(SyntaxFactory.ParseExpression(node.Expression.ToString().Replace(oldMethod, newMethod))) .WithArgumentList(SyntaxFactory.ParseArgumentList(newParameters)) .WithLeadingTrivia(node.GetLeadingTrivia()) .NormalizeWhitespace(); return node; } return ReplaceOnlyMethod; } /// /// This Method replaces only matching method in the invocation expression. For example, Math.Round(5.5) invocation expression matching on Round with a newMethod parameter of Abs and an oldMethod parameter of Round would return Math.Abs(5.5). /// /// The matching method in the invocation expression to be replaced. /// The new method to replace the old method with in the invocation expression. /// public Func GetReplaceMethodOnlyAction(string oldMethod, string newMethod) { //TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parseexpression ? InvocationExpressionSyntax ReplaceOnlyMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node) { node = node.WithExpression(SyntaxFactory.ParseExpression(node.Expression.ToString().Replace(oldMethod, newMethod))) .WithLeadingTrivia(node.GetLeadingTrivia()) .NormalizeWhitespace(); return node; } return ReplaceOnlyMethod; } /// /// This Method replaces only the parameters in the invocation expression. For example, Math.Round(5.5) invocation expression matching on Round with a parameter of (8) would return Math.Abs(8). /// /// The new invocation expression parameters to replace the old parameters with. /// public Func GetReplaceParametersOnlyAction(string newParameters) { //TODO what's the outcome if newMethod doesn't have a valid signature.. are there any options we could provide to parseexpression ? InvocationExpressionSyntax ReplaceOnlyMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node) { node = node.WithArgumentList(SyntaxFactory.ParseArgumentList(newParameters)).NormalizeWhitespace(); return node; } return ReplaceOnlyMethod; } public Func GetAppendMethodAction(string appendMethod) { InvocationExpressionSyntax ReplaceMethod(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node) { var operatorToken = node.DescendantTokens().FirstOrDefault(t => t.IsKind(SyntaxKind.DotToken)); node = SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.InvocationExpression(node), operatorToken, SyntaxFactory.IdentifierName(SyntaxFactory.ParseName(appendMethod).ToString()) ), SyntaxFactory.ArgumentList() ).NormalizeWhitespace(); return node; } return ReplaceMethod; } public Func GetAddCommentAction(string comment) { InvocationExpressionSyntax AddComment(SyntaxGenerator syntaxGenerator, InvocationExpressionSyntax node) { return (InvocationExpressionSyntax)CommentHelper.AddVBComment(node, comment); } return AddComment; } } }