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>

  

 

posted @ 2022-06-27 21:46  灰主流  阅读(324)  评论(0编辑  收藏  举报