最初の段階で、構文ノードに注釈を追加し、ノードを新しく生成されたノードに置き換えます。
第 2 段階では、変更されたドキュメント (注釈が追加された同じ構文ツリー) を分析していますが、SymbolInfo の参照はまだ変更されていない構文ノード (注釈なし) を参照しています。
注釈を追加した後に、ソリューションまたはプロジェクトを更新または再解析し、SymbolInfo を更新することは可能ですか?
1 つの 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);
}
}
}