C# Roslyn修改代码
源app
class Program
{
/// <summary>
/// 方法入口123
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Program.Plus(3,3);
var c1 = Plus(1, 2);
var c2 = Plus(3, 4);
var c3 = Plus(5,55);
var c4 = Plus(6, 666);
}
static int Plus(int a,int b)
{
return a + b;
}
}
}
roslyn代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.MSBuild;
namespace _2021_12_14_Roslyn批量修改代码
{
class Program
{
private static void Workspace_WorkspaceFailed(object sender,
Microsoft.CodeAnalysis.WorkspaceDiagnosticEventArgs e)
{
var x = e.Diagnostic.Message;
//Console.WriteLine(x);
//无法打开项目,因为语言“C#”不受支持
//Microsoft.CodeAnalysis
//处理文件“D:\测试代码\2021-12-14-Roslyn批量修改代码-测试解决方案\2021-12-14-Roslyn批量修改代码-测试解决方案\2021-12-14-Roslyn批量修改代码-测试解决方案.csproj”时 Msbuild 失败,出现消息: The tools version "Current" is unrecognized. Available tools versions are "14.0", "2.0", "3.5", "4.0". D:\测试代码\2021-12-14-Roslyn批量修改代码-测试解决方案\2021-12-14-Roslyn批量修改代码-测试解决方案\2021-12-14-Roslyn批量修改代码-测试解决方案.csproj
//修改build版本:
//Microsoft.Build 16.10.0
//Microsoft.Build.Framework 16.10.0
//Microsoft.Build.Locator 1.4.1
//Microsoft.CodeAnalysis.Workspaces.MSBuild 3.9.0
//Microsoft.Net.Compilers
}
static void Main(string[] args)
{
//安装 MSBuildLocator
MSBuildLocator.RegisterDefaults();
var solutionPath = @"D:\测试代码\2022-06-27-DemoApp\2022-06-27-DemoApp.sln";
var projectName = "2022-06-27-DemoApp";
var csFileName = "Program.cs";
var className1 = "Program";
CheckMethod(solutionPath, projectName, csFileName, className1, "Plus", "(int a,int b)", 1);
Console.WriteLine(DateTime.Now.ToString() + " 完成");
Console.Read();
}
private static void CheckMethod(string solutionPath, string projectName, string csFileName, string className1,
string methodName, string methodParListString, int maxArgumentCount)
{
//安装Microsoft.CodeAnalysis.Workspaces.MSBuild
//安装Microsoft.Build.Framework
using (var workspace = MSBuildWorkspace.Create())
{
workspace.SkipUnrecognizedProjects = true;
workspace.LoadMetadataForReferencedProjects = false;
workspace.WorkspaceFailed += Workspace_WorkspaceFailed;
// Selects a Solution File
//安装Microsoft.Build
var solution = workspace.OpenSolutionAsync(solutionPath).Result;
//项目库
var findProject = solution.Projects.FirstOrDefault(x => x.Name == projectName);
var csFileDoc = findProject.Documents.FirstOrDefault(x => x.Name == csFileName);
var semanticModel2 = csFileDoc.GetSemanticModelAsync().Result;
var rootNode2 = csFileDoc.GetSyntaxRootAsync().Result;
var nodeClass = rootNode2.DescendantNodes().OfType<ClassDeclarationSyntax>()
.FirstOrDefault(x => x.Identifier.ToString() == className1);
var nodeMethod = nodeClass.DescendantNodes()
.OfType<MethodDeclarationSyntax>().FirstOrDefault(x =>
x.Identifier.ToString() == methodName && x.ParameterList.ToString() == methodParListString);
var sampleMethodSymbol1 = semanticModel2.GetDeclaredSymbol(nodeMethod);
//查找引用
var referencesToSampleMethod2 = SymbolFinder.FindReferencesAsync(sampleMethodSymbol1, solution).Result;
var groupByFileName = GetReferenceFileLocations(referencesToSampleMethod2).GroupBy(x => x.Document.FilePath);
referencesToSampleMethod2.GroupBy(x => x.Locations.First().Document.Name);
foreach (var gpFile in groupByFileName)
{
Console.WriteLine(DateTime.Now.ToString() + " 个数=" + gpFile.Count() + " 找到引用源:" + gpFile.Key);
var first = gpFile.FirstOrDefault();
//文档
var docId = first.Document.Id;
var sourceTree = first.Location.SourceTree;
//root
var root = sourceTree.GetRoot();
var expressionFullTextList = GetExpressionFullText(gpFile.ToList(), root);
var reviter = new VisitExpressionStatementChanger(className1, methodName, expressionFullTextList, maxArgumentCount);
root = reviter.Visit(root);
solution = solution.WithDocumentSyntaxRoot(docId, root);
}
var result = workspace.TryApplyChanges(solution);
}
}
private static List<string> GetExpressionFullText(List<ReferenceLocation> refList, SyntaxNode root)
{
var expressionList = new List<string>();
foreach (var referenceLocation in refList)
{
var nodeFind = root.FindNode(referenceLocation.Location.SourceSpan);
//method node
var parentMethod = GetParentMethod<ExpressionStatementSyntax>(nodeFind);
if (parentMethod==null)
{
continue;
}
var txt = parentMethod.Expression.ToFullString();
expressionList.Add(txt);
}
return expressionList;
}
private static List<ReferenceLocation> GetReferenceFileLocations(IEnumerable<ReferencedSymbol> referencesToSampleMethod2)
{
var result = new List<ReferenceLocation>();
foreach (var referencedSymbol in referencesToSampleMethod2)
{
foreach (var location in referencedSymbol.Locations)
{
result.Add(location);
}
}
return result;
}
private static T GetParentMethod<T>(SyntaxNode nodeFind) where T : class
{
if (nodeFind.Parent == null)
{
return null;
}
if (nodeFind.Parent is T mm)
{
return mm;
}
else
{
return GetParentMethod<T>(nodeFind.Parent);
}
}
}
/// <summary>
/// The CSharpSyntaxRewriter allows to rewrite the Syntax of a node
/// </summary>
public class VisitExpressionStatementChanger : CSharpSyntaxRewriter
{
private readonly string _methodName;
private readonly int _argumentCountLimit;
private readonly string _className;
private readonly List<string> _referenceExpressionList;
public VisitExpressionStatementChanger(string methodClassName, string methodName, List<string> expressionFullTxtList, int argumentCountLimitValue)
{
_className = methodClassName;
_methodName = methodName;
_referenceExpressionList = expressionFullTxtList;
_argumentCountLimit = argumentCountLimitValue;
}
public override SyntaxNode VisitExpressionStatement(ExpressionStatementSyntax node)
{
var nodeParentIsMethod = _referenceExpressionList.Contains(node.Expression.ToFullString());
if (nodeParentIsMethod)
{
//Console.WriteLine(node.Expression.ToFullString());
var leadingTrivia = node.GetLeadingTrivia();
var trailingTrivia = node.GetTrailingTrivia();
//判断参数是不是多于2个
var invocationFirst = node.DescendantNodes().OfType<InvocationExpressionSyntax>().First();
var argumentList = invocationFirst.ArgumentList.Arguments.ToList();
if (argumentList.Count > _argumentCountLimit)
{
//计算空格
var firstToken = node.DescendantTokens().First();
var whiteSpaceTrivia = firstToken.LeadingTrivia.FirstOrDefault(x => x.IsKind(SyntaxKind.WhitespaceTrivia));
var whiteSpaceLength = whiteSpaceTrivia.FullSpan.Length;
var whiteSpace = string.Join("", Enumerable.Range(1, whiteSpaceLength).Select(x => " "));
if (_argumentCountLimit <= 0)
{
//不要参数,所有参数都忽略
var replaceNode = SyntaxFactory.ParseStatement($"{whiteSpace}{_className}.{_methodName}();\r\n");
Console.WriteLine("忽略参数:" + node.Expression + " 到新的->>>>>" + replaceNode.ToFullString().Replace('\r', ' ').Replace('\n', '\\'));
return replaceNode;
}
else if (_argumentCountLimit >= 1)
{
var firstParam = argumentList[0].Expression;
var secondParam = argumentList[1].Expression;
var replaceNode = SyntaxFactory.ParseStatement($"{leadingTrivia.ToFullString()}var a = (int)({firstParam});\r\n{whiteSpace}var b= (int)({secondParam});\r\n {whiteSpace}{_className}.{_methodName}(a,b);{trailingTrivia.ToFullString()}\r\n");
Console.WriteLine("修改参数:" + node.Expression + " 到新的->>>>>" + replaceNode.ToFullString().Replace('\r', ' ').Replace('\n', '\\'));
return replaceNode;
}
}
}
return base.VisitExpressionStatement(node);
}
}
}
效果:
class Program
{
/// <summary>
/// 方法入口123
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
var a = (int)(3);
var b= (int)(3);
Program.Plus(a,b);
var c1 = Plus(1, 2);
var c2 = Plus(3, 4);
var c3 = Plus(5,55);
var c4 = Plus(6, 666);
}
static int Plus(int a,int b)
{
return a + b;
}
}
nuget包:
<?xml version="1.0" encoding="utf-8"?> <packages> <package id="Humanizer.Core" version="2.13.14" targetFramework="net472" /> <package id="Microsoft.Bcl.AsyncInterfaces" version="6.0.0" targetFramework="net472" /> <package id="Microsoft.Build" version="16.10.0" targetFramework="net472" /> <package id="Microsoft.Build.Framework" version="16.10.0" targetFramework="net472" /> <package id="Microsoft.Build.Locator" version="1.4.1" targetFramework="net462" /> <package id="Microsoft.CodeAnalysis" version="3.9.0" targetFramework="net472" /> <package id="Microsoft.CodeAnalysis.Analyzers" version="3.3.3" targetFramework="net472" developmentDependency="true" /> <package id="Microsoft.CodeAnalysis.Common" version="3.9.0" targetFramework="net472" /> <package id="Microsoft.CodeAnalysis.CSharp" version="3.9.0" targetFramework="net472" /> <package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="3.9.0" targetFramework="net472" /> <package id="Microsoft.CodeAnalysis.VisualBasic" version="3.9.0" targetFramework="net472" /> <package id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="3.9.0" targetFramework="net472" /> <package id="Microsoft.CodeAnalysis.Workspaces.Common" version="3.9.0" targetFramework="net472" /> <package id="Microsoft.CodeAnalysis.Workspaces.MSBuild" version="3.9.0" targetFramework="net472" /> <package id="Microsoft.Net.Compilers" version="4.0.1" targetFramework="net472" developmentDependency="true" /> <package id="Microsoft.NET.StringTools" version="1.0.0" targetFramework="net472" /> <package id="Microsoft.VisualStudio.Setup.Configuration.Interop" version="3.0.4492" targetFramework="net472" developmentDependency="true" /> <package id="System.Buffers" version="4.5.1" targetFramework="net472" /> <package id="System.Collections.Immutable" version="6.0.0" targetFramework="net472" /> <package id="System.Composition" version="6.0.0" targetFramework="net472" /> <package id="System.Composition.AttributedModel" version="6.0.0" targetFramework="net472" /> <package id="System.Composition.Convention" version="6.0.0" targetFramework="net472" /> <package id="System.Composition.Hosting" version="6.0.0" targetFramework="net472" /> <package id="System.Composition.Runtime" version="6.0.0" targetFramework="net472" /> <package id="System.Composition.TypedParts" version="6.0.0" targetFramework="net472" /> <package id="System.Configuration.ConfigurationManager" version="6.0.0" targetFramework="net472" /> <package id="System.IO.Pipelines" version="6.0.0" targetFramework="net472" /> <package id="System.Memory" version="4.5.4" targetFramework="net472" /> <package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" /> <package id="System.Reflection.Metadata" version="6.0.0" targetFramework="net472" /> <package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net472" /> <package id="System.Security.AccessControl" version="6.0.0" targetFramework="net472" /> <package id="System.Security.Permissions" version="6.0.0" targetFramework="net472" /> <package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net472" /> <package id="System.Text.Encoding.CodePages" version="6.0.0" targetFramework="net472" /> <package id="System.Text.Encodings.Web" version="6.0.0" targetFramework="net472" /> <package id="System.Text.Json" version="6.0.0" targetFramework="net472" /> <package id="System.Threading.Tasks.Dataflow" version="6.0.0" targetFramework="net472" /> <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net472" /> <package id="System.ValueTuple" version="4.5.0" targetFramework="net472" /> </packages>
浙公网安备 33010602011771号