c# - 有条件地在 Roslyn 代码修复中添加“使用”语句
问题描述
我正在使用 .NET 编译器 API 在 Roslyn 中编写一些代码分析器/代码修复器。我希望代码修复来转换以下代码:
string.Format("{0} {1}", A, B)
至
StringExtensions.SafeJoin(" ", A, B)
到目前为止,我有这个代码:
private async Task<Document> UseJoinAsync(Document document, InvocationExpressionSyntax invocationExpr, CancellationToken cancellationToken)
{
var argumentList = invocationExpr.ArgumentList;
var firstArgument = argumentList.Arguments[1];
var secondArgument = argumentList.Arguments[2];
var statement =
InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName("StringExtensions"), // requires using Trilogy.Miscellaneous
IdentifierName("SafeJoin")))
.WithArgumentList(
ArgumentList(
SeparatedList<ArgumentSyntax>(
new SyntaxNodeOrToken[]
{
Argument(
LiteralExpression(
SyntaxKind.StringLiteralExpression,
Literal(" "))),
Token(SyntaxKind.CommaToken),
firstArgument,
Token(SyntaxKind.CommaToken),
secondArgument
}))).WithLeadingTrivia(invocationExpr.GetLeadingTrivia()).WithTrailingTrivia(invocationExpr.GetTrailingTrivia())
.WithAdditionalAnnotations(Formatter.Annotation);
var root = await document.GetSyntaxRootAsync(cancellationToken);
var newRoot = root.ReplaceNode(invocationExpr, statement);
var newDocument = document.WithSyntaxRoot(newRoot);
return newDocument;
}
但是,我有两个悬而未决的问题:
1)如何将所需的内容添加using Trilogy.Miscellaneous
到文件的顶部。
和
2)如何检测我的项目是否引用了所需的程序集。在这种情况下,如果我的程序集Trilogy.Common
没有被引用,我要么不提供代码修复,要么提出建议string.Join(" ", A, B)
而不是我自己的SafeJoin
实现。
更新
我通过如下更新我的代码解决了#1...
var newRoot = root.ReplaceNode(invocationExpr, statement);
// Iterate through our usings to see if we've got what we need...
if (root?.Usings.Any(u => u.Name.ToString() == "Trilogy.Miscellaneous") == false)
{
// Create and add the using statement...
var usingStatement = UsingDirective(QualifiedName(IdentifierName("Trilogy"), IdentifierName("Miscellaneous")));
newRoot = newRoot.AddUsings(usingStatement);
}
var newDocument = document.WithSyntaxRoot(newRoot);
return newDocument;
仍然希望对第 2 项有所帮助。
解决方案
我最终向 RegisterCodeFixesAsync 方法添加了一些代码。我觉得对我所需的程序集的测试有点像 hack,所以如果有人发布一个问题,我会接受这个问题的更好答案。
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
// Find the type invocation expression identified by the diagnostic.
var invocationExpr = root.FindToken(
diagnosticSpan.Start).Parent.AncestorsAndSelf()
.OfType<InvocationExpressionSyntax>().First();
// Get the symantec model from the document
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
// Check for the assembly we need. I suspect there is a better way...
var hasAssembly = semanticModel.Compilation.ExternalReferences.Any(er => er.Properties.Kind == MetadataImageKind.Assembly && er.Display.EndsWith("Trilogy.Common.dll"));
// Register a code action that will invoke the fix, but only
// if we have the assembly that we need
if (hasAssembly)
context.RegisterCodeFix(
CodeAction.Create(title, c => UseJoinAsync(
context.Document, invocationExpr, c), equivalenceKey: title), diagnostic);
}
推荐阅读
- spring-boot - 如何在同一个端口上运行多个微服务?
- node.js - 从变量而不是文件节点 JS 解析 CSV
- hibernate - #无法从表中获取数据
- excel - 选择、导入和文本分隔 Txt 文件
- image - 将 3D numpy 数组视为 3D 图像
- apache-kafka - 无法从 ksqldb 中的 kafka 主题读取消息
- javascript - 模拟 fs.existsSync
- java - Java - 迭代 + 实例化类目录
- wpf - 如何使用 WindowChrome 在 WPF 自定义标题栏上显示默认按钮
- php - Symfony 4.4 中 SonataFormatter + SonataNews 的 CKEditor 问题