我正在尝试在 Roslyn 中执行语法转换。 AST 重新排列工作正常,但是,当我更改内容时,我需要获得更新的
SemanticModel
,以反映我的 SyntaxTree
的新结构。
这部分有效,我可以这么说,因为我已经做了大量的调试。
CSharpSyntaxTree tree = RetrieveTree(); // Just successfully gets the original tree
CSharpCompilation compilation = CSharpCompilation.Create(...); // Retrieves the compilation object
CompilationUnitSyntax node = tree.GetRoot();
// Doing stuff to change the tree
node = node.RemoveNodes(...);
node = node.AddMembers(...);
这样我就可以得到新树了:
CSharpSyntaxTree newTree = node.SyntaxTree as CSharpSyntaxTree;
通过调试,我可以看到
newTree
具有新结构,而 tree
具有旧结构。我还可以成功遍历 newTree
并在 AST 上进行操作。这样转型就成功了。
我认为当我尝试更新 CSharpCompilation
以及当我从中取出
SemanticModel
时,问题就从这里开始。
CSharpCompilation newCompilation = compilation.ReplaceSyntaxTree(tree, newTree);
SemanticModel newSemanticModel = newCompilation.GetSemanticModel(newTree);
因为当我尝试时:
TypeSyntax myClassTypeNode = GetSourceCodeClassTypeNode(); // Just successfully gets a node
var symbol = newSemanticModel.getSymbolInfo(myClassTypeNode).Symbol;
则
symbol
为空。当然,如果我尝试使用原来的null
,它也会保持SemanticModel
。
无需转换即可工作如果我不执行转换并使用旧的
SemanticModel
,则符号已成功检索!
我做错了什么?
这是一个迟到的答案,但我希望它对某人有用。根据我的经验,如果用
SyntaxNode.ReplaceNode
替换节点并用 CompilationReplaceSyntaxTree
更新编译后无法从语义模型中获取符号,那是因为替换节点时引入了错误。当插入使用 SyntaxFactory
API 创建的节点时,很容易引入微妙的错误,这些节点看起来正确(但实际上并非如此),因为一些相似的节点发出完全相同的代码,并且替换函数将非常宽松只是为了稍后在语义分析中发现错误。我发现的一个技巧是使用以下扩展方法来测试编译是否有错误:
public static List<string> GetCompilationErrors(this Compilation compilation)
{
var diagnostics = compilation.GetDiagnostics()
.Where(d => d.Severity == DiagnosticSeverity.Error)
.Select(d => $"{d.Location}, {d.Id}: {d.GetMessage()}")
.ToList();
return diagnostics;
}
假设树操作在语义上是正确的,如果上述函数没有检测到错误,那么语义模型应该产生预期的结果。