Reputation: 69918
I have written the program below that can identify strings in both variables and parameters for methods. What I would like to do is replace these strings with a method call to a new method I'm adding to the program.
Ex:
I'm now adding this metod to the code:
public static string StringManipulation(string test)
{
return test + " new value";
}
I would then like to replace:
var test = ""test var"";
To:
var test = StringManipulation(""test var"");
And:
Test(""Testing"");
To:
Test(StringManipulation(""Testing""));
I have found a lot of answers on how to add or replace entire nodes but in this case I wan't to reuse some values and I haven't found a good answer to this.
Code:
class Program
{
static void Main(string[] args)
{
var workspace = new AdhocWorkspace();
var projectId = ProjectId.CreateNewId();
var versionStamp = VersionStamp.Create();
var projectInfo = ProjectInfo.Create(projectId, versionStamp, "NewProject", "projName", LanguageNames.CSharp);
var newProject = workspace.AddProject(projectInfo);
var sourceText = SourceText.From(
@"using System;
using System.Collections;
using System.Linq;
using System.Text;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
var test = ""test var"";
string test1 = ""test string"";
String test2 = ""test String"";
const string test3 = ""test const"";
readonly string test4 = ""test readonly"";
int i = 0;
var i2 = 0;
Test(""Testing"");
Test(""Testing"", ""Testing 2"", 1);
}
public static string Test(string test)
{
return test;
}
public static string Test(string test, string test2, int test3)
{
return test + test2 + test3;
}
}
}");
var document = workspace.AddDocument(newProject.Id, "NewFile.cs", sourceText);
var syntaxRoot = document.GetSyntaxRootAsync().Result;
var root = (CompilationUnitSyntax)syntaxRoot;
var invocationExpressions = root.DescendantNodes()
.OfType<InvocationExpressionSyntax>();
var mainNode = root.DescendantNodes()
.OfType<MethodDeclarationSyntax>().FirstOrDefault(x => x.Identifier.ValueText == "Main"
&& x.ParameterList.Parameters.FirstOrDefault().Identifier.ValueText == "args");
var editor = DocumentEditor.CreateAsync(document).Result;
SeparatedSyntaxList<ParameterSyntax> parametersList = new SeparatedSyntaxList<ParameterSyntax>().AddRange
(new ParameterSyntax[]
{
SyntaxFactory.Parameter(SyntaxFactory.Identifier("test")).WithType(SyntaxFactory.ParseTypeName("string")),
}
);
var syntax = SyntaxFactory.ParseStatement("return test + \" new value\";");
var newMethod = SyntaxFactory.MethodDeclaration(
SyntaxFactory.List<AttributeListSyntax>(),
SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)),
SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)),
null,
SyntaxFactory.Identifier("StringManipulation"),
null,
SyntaxFactory.ParameterList(parametersList),
SyntaxFactory.List<TypeParameterConstraintClauseSyntax>(),
SyntaxFactory.Block(syntax),
null
);
newMethod = newMethod.NormalizeWhitespace();
editor.InsertAfter(mainNode, newMethod);
foreach (var invocationExpressionSyntax in invocationExpressions)
{
foreach (var argument in invocationExpressionSyntax.ArgumentList.Arguments)
{
if (argument.Expression.Kind() == SyntaxKind.StringLiteralExpression)
{
Console.WriteLine($"Method: {invocationExpressionSyntax.Expression.GetFirstToken().Value} Parameter: {argument.Expression.GetFirstToken().Value}");
}
}
}
var localDeclaration = new LocalDeclarationVirtualizationVisitor();
localDeclaration.Visit(root);
var localDeclarations = localDeclaration.LocalDeclarations;
foreach (var localDeclarationStatementSyntax in localDeclarations)
{
foreach (VariableDeclaratorSyntax variable in localDeclarationStatementSyntax.Declaration.Variables)
{
var stringKind = variable.Initializer.Value.Kind();
if (stringKind == SyntaxKind.StringLiteralExpression)
{
Console.WriteLine($"Key: {variable.Identifier.Value} Value:{variable.Initializer.Value}");
}
}
}
var newDocument = editor.GetChangedDocument();
}
}
class LocalDeclarationVirtualizationVisitor : CSharpSyntaxRewriter
{
public LocalDeclarationVirtualizationVisitor()
{
LocalDeclarations = new List<LocalDeclarationStatementSyntax>();
}
public List<LocalDeclarationStatementSyntax> LocalDeclarations { get; set; }
public override SyntaxNode VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
{
node = (LocalDeclarationStatementSyntax)base.VisitLocalDeclarationStatement(node);
LocalDeclarations.Add(node);
return node;
}
}
Upvotes: 0
Views: 1632
Reputation: 69918
Code for modifying method calls:
foreach (var invocationExpressionSyntax in invocationExpressions)
{
if (invocationExpressionSyntax.ArgumentList.Arguments.Any(x =>
x.Expression.Kind() == SyntaxKind.StringLiteralExpression))
{
var stringList = new List<string>();
for (int i = 0; i < invocationExpressionSyntax.ArgumentList.Arguments.Count(); i++)
{
if (invocationExpressionSyntax.ArgumentList.Arguments[i].Expression.Kind() == SyntaxKind.StringLiteralExpression)
{
stringList.Add("StringManipulation(\"" + invocationExpressionSyntax.ArgumentList.Arguments[i].Expression.GetFirstToken().ValueText + "\")");
}
else
{
stringList.Add(invocationExpressionSyntax.ArgumentList.Arguments[i].Expression
.GetFirstToken().ValueText);
}
}
SeparatedSyntaxList<ArgumentSyntax> arguments = new SeparatedSyntaxList<ArgumentSyntax>().AddRange
(new ArgumentSyntax[]
{
SyntaxFactory.Argument(SyntaxFactory.ParseExpression($"{string.Join(",", stringList)}")),
}
);
var newMethodWithStringObfuscation =
SyntaxFactory
.InvocationExpression(SyntaxFactory.IdentifierName(invocationExpressionSyntax.Expression
.GetFirstToken().ValueText))
.WithArgumentList(
SyntaxFactory.ArgumentList()
.WithOpenParenToken(
SyntaxFactory.Token(
SyntaxKind.OpenParenToken))
.WithArguments(arguments)
.WithCloseParenToken(
SyntaxFactory.Token(
SyntaxKind.CloseParenToken)));
Console.WriteLine($"Replacing values for method {invocationExpressionSyntax.Expression.GetFirstToken().ValueText}");
editor.ReplaceNode(invocationExpressionSyntax, newMethodWithStringObfuscation);
}
}
Code for modifying variables:
foreach (var localDeclarationStatementSyntax in localDeclarations)
{
foreach (VariableDeclaratorSyntax variable in localDeclarationStatementSyntax.Declaration.Variables)
{
var stringKind = variable.Initializer.Value.Kind();
if (stringKind == SyntaxKind.StringLiteralExpression)
{
var newVariable = SyntaxFactory.ParseStatement($"string {variable.Identifier.ValueText} = StringManipulation({variable.Initializer.Value});");
newVariable.NormalizeWhitespace();
editor.ReplaceNode(variable, newVariable);
Console.WriteLine($"Key: {variable.Identifier.Value} Value:{variable.Initializer.Value}");
}
}
}
Upvotes: 3