如何在添加注释后更新SymbolInfo中对SyntaxNode的引用?

时间:2013-05-03 16:00:11

标签: c# roslyn

在第一阶段,我正在为语法节点添加注释,并用新生成的节点替换节点。

在我分析修改后的文档(添加注释的语法树相同)的第二阶段,但是SymbolInfo中的引用仍然引用未修改的语法节点(没有注释)。

是否可以在添加注释后更新或重新分析解决方案或项目并更新SymbolInfo?

使用一个C#文件创建简单的解决方案:

class С
{
    void g()
    { }

    void f()
    {
        g();
    }
}

尝试用程序解析它:

using System.Collections.Generic;
using Roslyn.Compilers;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;

namespace RoslynExample2
{
    class Program
    {
        static void Main(string[] args)
        {
            var workspace = Workspace.LoadSolution(@"..\..\..\..\RoslynExampleTest\RoslynExampleTest.sln");
            var solution = workspace.CurrentSolution;

            foreach (var project in solution.Projects)
            {
                Annotator annotator = new Annotator();
                foreach (var document in project.Documents)
                {
                    CompilationUnitSyntax compilationUnit = (CompilationUnitSyntax)document.GetSyntaxRoot();
                    var mcu = annotator.AddAnnotations(compilationUnit);
                    document.UpdateSyntaxRoot(mcu);
                }
            }

            foreach (var project in solution.Projects)
            {
                foreach (var document in project.Documents)
                {
                    var compilationUnit = document.GetSyntaxRoot();
                    var semanticModel = document.GetSemanticModel();

                    MySyntaxWalker sw = new MySyntaxWalker(semanticModel);
                    sw.Visit((SyntaxNode)compilationUnit);
                }
            }
        }
    }

    internal class Annotator
    {
        internal struct SyntaxNodeTuple
        {
            internal SyntaxNode Origin;
            internal SyntaxNode Modified;

            internal SyntaxNodeTuple(SyntaxNode origin, SyntaxNode modified)
            {
                Origin = origin;
                Modified = modified;
            }
        }

        private SyntaxNodeTuple AddAnnotation(SyntaxNode s)
        {
            SyntaxNodeTuple t;

            switch (s.Kind)
            {
                case SyntaxKind.ClassDeclaration:
                    t = AddAnnotations((ClassDeclarationSyntax)s);
                    break;
                case SyntaxKind.MethodDeclaration:
                    t = AddAnnotations((MethodDeclarationSyntax)s);
                    break;
                default:
                    t = new SyntaxNodeTuple();
                    break;
            }

            return t;
        }

        private static T ReplaceNodes<T>(T d, List<SyntaxNodeTuple> tuples)
            where T : SyntaxNode
        {
            T d2 = d;
            foreach (var t in tuples)
            {
                d2 = d2.ReplaceNode(t.Origin, t.Modified);
            }
            return d2;
        }

        private void AddAnnotationsToList(SyntaxList<MemberDeclarationSyntax> list, List<SyntaxNodeTuple> tuples)
        {
            foreach (var m in list)
            {
                tuples.Add(AddAnnotation(m));
            }
        }

        internal CompilationUnitSyntax AddAnnotations(CompilationUnitSyntax d)
        {
            List<SyntaxNodeTuple> tuples = new List<SyntaxNodeTuple>();
            AddAnnotationsToList(d.Members, tuples);
            var d2 = ReplaceNodes(d, tuples);
            return d2;
        }

        internal SyntaxNodeTuple AddAnnotations(ClassDeclarationSyntax d)
        {
            List<SyntaxNodeTuple> tuples = new List<SyntaxNodeTuple>();
            AddAnnotationsToList(d.Members, tuples);
            var d2 = ReplaceNodes(d, tuples);
            d2 = d2.WithAdditionalAnnotations(new MyAnnotation());
            return new SyntaxNodeTuple(d, d2);
        }

        internal SyntaxNodeTuple AddAnnotations(MethodDeclarationSyntax d)
        {
            var d2 = d.WithAdditionalAnnotations(new MyAnnotation());

            bool hasAnnotation = d2.HasAnnotations(typeof(MyAnnotation)); // annotation exists

            return new SyntaxNodeTuple(d, d2);
        }
    }

    class MyAnnotation : SyntaxAnnotation
    { }

    partial class MySyntaxWalker : SyntaxWalker
    {
        private ISemanticModel _semanticModel;

        public MySyntaxWalker(ISemanticModel semanticModel)
        {
            _semanticModel = semanticModel;
        }

        public override void VisitInvocationExpression(InvocationExpressionSyntax decl)
        {
            var si = _semanticModel.GetSymbolInfo(decl);
            var dsns = si.Symbol.DeclaringSyntaxNodes;
            var dsn0 = dsns[0];
            bool hasAnnotation = dsn0.HasAnnotations(typeof(MyAnnotation));  // annotation doesn't exists
        }
    }
}

更新后的变种:

using System;
using System.Diagnostics;
using Roslyn.Compilers;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;

namespace RoslynExample2
{
    class Program
    {
        static void Main(string[] args)
        {
            var workspace = Workspace.LoadSolution(@"..\..\..\..\RoslynExampleTest\RoslynExampleTest.sln");
            var solution = workspace.CurrentSolution;

            foreach (var projectId in solution.ProjectIds)
            {
                var project = solution.GetProject(projectId);
                foreach (var documentId in project.DocumentIds)
                {
                    var document = project.GetDocument(documentId);
                    CompilationUnitSyntax compilationUnit = (CompilationUnitSyntax)document.GetSyntaxRoot();
                    Debug.WriteLine(String.Format("compilationUnit={0} before", compilationUnit.GetHashCode()));
                    Debug.WriteLine(String.Format("project={0} before", project.GetHashCode()));
                    Debug.WriteLine(String.Format("solution={0} before", solution.GetHashCode()));
                    var mcu = new AnnotatorSyntaxRewritter().Visit(compilationUnit);
                    var project2 = document.UpdateSyntaxRoot(mcu).Project;
                    if (mcu != compilationUnit)
                    {
                        solution = project2.Solution;
                    }
                    Debug.WriteLine(String.Format("compilationUnit={0} after", mcu.GetHashCode()));
                    Debug.WriteLine(String.Format("project={0} after", project2.GetHashCode()));
                    Debug.WriteLine(String.Format("solution={0} after", solution.GetHashCode()));
                }
            }

            foreach (var projectId in solution.ProjectIds)
            {
                var project = solution.GetProject(projectId);
                foreach (var documentId in project.DocumentIds)
                {
                    var document = project.GetDocument(documentId);
                    var compilationUnit = document.GetSyntaxRoot();
                    var semanticModel = document.GetSemanticModel();
                    Debug.WriteLine(String.Format("compilationUnit={0} stage", compilationUnit.GetHashCode()));
                    Debug.WriteLine(String.Format("project={0} stage", project.GetHashCode()));
                    Debug.WriteLine(String.Format("solution={0}", solution.GetHashCode()));

                    MySyntaxWalker sw = new MySyntaxWalker(semanticModel);
                    sw.Visit((SyntaxNode)compilationUnit);
                }
            }
        }
    }

    class AnnotatorSyntaxRewritter : SyntaxRewriter
    {
        public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
        {
            node = node.WithAdditionalAnnotations(new MyAnnotation());
            return base.VisitMethodDeclaration(node);
        }
    }

    class MyAnnotation : SyntaxAnnotation
    { }

    partial class MySyntaxWalker : SyntaxWalker
    {
        private ISemanticModel _semanticModel;

        public MySyntaxWalker(ISemanticModel semanticModel)
        {
            _semanticModel = semanticModel;
        }

        public override void VisitMethodDeclaration(MethodDeclarationSyntax decl)
        {
            bool hasAnnotation = decl.HasAnnotations(typeof(MyAnnotation));
            Debug.Assert(hasAnnotation);
        }
    }
}

1 个答案:

答案 0 :(得分:2)

您的示例中的问题是变量solution是一个不可变对象,它在第一次加载时引用解决方案。在您调用document.UpdateSyntaxRoot(mcu)的代码中,实际创建并返回新的IDocument,该IProject位于新的ISolution中。

尝试将该位代码更改为:

Annotator annotator = new Annotator();
foreach (var projectId in solution.ProjectIds)
{
    foreach (var documentId in solution.GetProject(projectId).DocumentIds)
    {
        var document = solution.GetProject(projectId).GetDocument(documentId);
        CompilationUnitSyntax compilationUnit = (CompilationUnitSyntax)document.GetSyntaxRoot();
        var mcu = annotator.AddAnnotations(compilationUnit);
        solution = document.UpdateSyntaxRoot(mcu).Project.Solution;
    }
}