Roslyn-通过替换语法根

时间:2018-07-27 08:31:07

标签: c# roslyn diagnostics roslyn-code-analysis

我这里有一些特定的内容,基本上,我正在加载一个临时工作区并在编译中得到与特定值有关的错误,因为我确切地知道我需要替换什么文本,而不是在哪里。下面的代码。

public static async Task<Solution> UpdateEntityReferences(Solution solution, ProjectId servicesId, string oldValue, string newValue)
{
     var project = solution.GetProject(servicesId);
     var compilation = await project.GetCompilationAsync();
     var diagnostics = compilation.GetDiagnostics().Where(diag => diag.GetMessage().Contains($"'{oldValue}'"));

     foreach (var diagnostic in diagnostics)
     {
         var errorLineSpan = diagnostic.Location.GetLineSpan();
         var document = project.FindDocumentByName(Path.GetFileName(errorLineSpan.Path));
         var syntaxRoot = await document.GetSyntaxRootAsync();
         var errorSpan = errorLineSpan.Span;
     }
     return solution;
}

因此,到目前为止,我已经找到了错误的位置,并且基本上想返回该文档的新版本,但将“ errorSpan”替换为“ newValue”文本,但找不到解决方法,这可能吗?

编辑:借助Get the SyntaxNode given the linenumber in a SyntaxTree,我能够从SyntaxTree获取SyntaxNode,并且应该能够替换文本范围(for循环变为下面的内容),但这不起作用。

foreach (var diagnostic in diagnostics)
{
    var errorLineSpan = diagnostic.Location.GetLineSpan();
    var document = project.FindDocumentByName(Path.GetFileName(errorLineSpan.Path));
    var syntaxTree = await document.GetSyntaxTreeAsync();

    var errorSpan = errorLineSpan.Span;
    var lineSpan = syntaxTree.GetText().Lines[errorSpan.Start.Line].Span;

    var node = syntaxTree.GetRoot().DescendantNodes(lineSpan)
                .First(n => lineSpan.Contains(n.FullSpan));

    var errorTextSpan = TextSpan.FromBounds(errorSpan.Start.Character, errorSpan.End.Character);

    var newNodeText = node.GetText().Replace(errorTextSpan, newValue);
}

替换文本(错误时间的一半),然后剩下一个SourceText对象,我无法弄清楚如何在文档中替换文本。有什么想法吗?

1 个答案:

答案 0 :(得分:0)

最后到那里-因此,如果不使用document editor之类的东西,就不能替换textspan并取回文档,因为您需要替换SyntaxNode并且不能保证LineSpan的字符位置仍然正确的节点被抓住。

解决方案是简单地使用LineSpan中的行号来抓住该行上的节点,然后使用LINQ向下过滤到令牌。一旦获取了确切的节点,就可以在父节点中替换它,然后在syntaxRoot中替换,最后在文档中替换。下面的代码。

foreach (var diagnostic in diagnostics)
{
     servicesProject = solution.GetProject(servicesId);
     var errorLineSpan = diagnostic.Location.GetLineSpan();
     var document = servicesProject.FindDocumentByName(Path.GetFileName(errorLineSpan.Path));
     var syntaxTree = await document.GetSyntaxTreeAsync();
     var errorSpan = errorLineSpan.Span;
     var lineSpan = syntaxTree.GetText().Lines[errorSpan.Start.Line].Span;

     var node = syntaxTree.GetRoot().DescendantNodes(lineSpan)
.First(n => lineSpan.Contains(n.FullSpan)).DescendantNodes()
.OfType<IdentifierNameSyntax>().FirstOrDefault(c => c.Identifier.Text == oldValue);

        var newNode = node.ReplaceNode(node, SyntaxFactory.IdentifierName(newValue));
        var newSyntaxRoot = syntaxTree.GetRoot().ReplaceNode(node, newNode);
        document = document.WithSyntaxRoot(newSyntaxRoot);
        solution = document.Project.Solution;           
}