C# 如何更新对SyntaxNode的引用';添加批注后,是否在SymbolInfo中添加了?

C# 如何更新对SyntaxNode的引用';添加批注后,是否在SymbolInfo中添加了?,c#,roslyn,C#,Roslyn,在第一阶段中,我向语法节点添加注释,并用新生成的节点替换节点 在第二个阶段,当我分析修改后的文档(相同的语法树,添加了注释)时,SymbolInfo中的引用仍然引用未修改的语法节点(没有注释) 是否可以在添加注释后更新或重新分析解决方案或项目并更新SymbolInfo 使用一个C#文件创建简单解决方案: 并尝试使用以下程序对其进行解析: using System.Collections.Generic; using Roslyn.Compilers; using Roslyn.Compilers

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

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

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

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

并尝试使用以下程序对其进行解析:

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
        }
    }
}

示例中的问题是变量
solution
是一个不可变的对象,它在首次加载解决方案时引用该解决方案。在调用
document.UpdateSyntaxRoot(mcu)
的代码中,实际创建并返回一个新的
i文档
,该文档位于新的
i项目
,该项目位于新的
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;
    }
}

可以在添加注释的地方包含代码吗?添加注释后是否会获得新的编译和语义模型?是的,在调用IDocument.UpdateSyntaxRoot()后,我会再次获得文档和语义模型。我将添加最小的代码,以尽快重现问题。谢谢凯文。你真是帮了我大忙。你好,凯文。还有一个问题。如果项目包含2个C#文件,并且第一个文件中的AST已修改,第二个文件中的AST未修改,则document.UpdateSyntaxRoot(mcu)。第二个文件的项目返回对未修改解决方案的引用。这是通过设计实现的。如果使用相同的根进行更新,我们将返回相同的解决方案,以避免不必要的分配。在这种情况下,如果您想要一个“新的”解决方案实例,可以使用solution.Clone()。我只想为所有项目和文档中的每个方法和类型添加注释。然后我想得到包含所有修改的解决方案的引用。但我仍然无法编写正确的示例来实现这一点(请参阅更新版本)。首先,我尝试忽略document.UpdateSyntaxRoot(mcu)返回的值,如果语法根没有修改,但这没有帮助。你能告诉我什么时候必须调用Solution.Clone()来解决这个问题吗?你没有看到调用Solution.Clone()。这个问题是另一个微妙的不变性问题。问题在于,在内部循环中,文档总是从原始项目中检索,而不是从使用新文档更新的项目中检索。我已经将我的代码示例更新为当项目中有多个文档时应该可以使用的代码示例。
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);
        }
    }
}
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;
    }
}