Roslyn CodeFix - MSBuild Properties/metadata 和单元测试
Roslyn CodeFix - MSBuild Properties/metadata and Unit testing
我正在构建 roslyn analyzer/code 修复程序,但我不想访问 MSBuild 属性和元数据(来自 Directory.build.props 和 .csproj)以了解如何应用代码修复。我只找到了在源代码生成器中执行此操作的文档,但在分析器中找不到。
更具体地说,我想知道项目是否配置为使用新的 ImplicitUsings,但如果能访问所有内容也会很有用。
还有我们有什么办法让所有的项目全局使用吗?
并且使用新的 Microsoft.CodeAnalysis.Testing 如何添加 MSBuild 属性 以便我可以实际测试它?
此致。
在 DiagnosticAnalyzer
s 中访问 MSBuild 属性和元数据实际上与在 ISourceGenerator
s/[=20 中读取和测试它们的方式非常相似=]s,因为 Source Generators 在技术上也是 .NET 分析器 。
我假设您提到的文档是 Source Generators Cookbook.
首先,我们需要使 MSBuild 属性 可用于分析器的全局分析器配置选项:
<Project>
<ItemGroup>
<CompilerVisibleProperty Include="MyAnalyzer_MyProperty" />
</ItemGroup>
</Project>
然后,我们可以从 AnalyzerConfigOptionsProvider 的 GlobalOptions
中读取 属性 的值。您会在您选择的 AnalysisContext.Register*
方法的参数中找到它,您在覆盖的 DiagnosticAnalyzer.Initialize(AnalysisContext)
方法中使用该方法。
例如 RegisterCompilationAction
:
bool isEnabled = false;
if (compilationAnalysisContext.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value))
{
isEnabled = value.Equals("true", StringComparison.OrdinalIgnoreCase) || value.Equals("enable", StringComparison.OrdinalIgnoreCase);
}
ImmutableDictionary<string, string?>.Builder properties = ImmutableDictionary.CreateBuilder<string, string?>();
if (isEnabled)
{
properties.Add("IsEnabled", value);
}
var diagnostic = Diagnostic.Create(Rule, location, properties.ToImmutable());
compilationAnalysisContext.ReportDiagnostic(diagnostic);
CodeFixProvider
的 CodeFixContext
没有专用的 AnalyzerOptions Options
属性,但您可以通过 Diagnostic.Properties
:
foreach (Diagnostic diagnostic in context.Diagnostics)
{
if (diagnostic.Properties.TryGetValue("IsEnabled", out string? value))
{
var action = CodeAction.Create(Title, cancellationToken => OnCreateChangedDocument(context.Document, cancellationToken), diagnostic.Id);
context.RegisterCodeFix(action, diagnostic);
}
}
... 或者,我刚刚在撰写此答案时发现的,通过 CodeFixContext.Document.Project.AnalyzerOptions
访问 AnalyzerConfigOptionsProvider
。这适用于任何可用的 Document
(或 Project
):
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
bool hasValue = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value);
}
此外,它适用于 CodeRefactoringProvider
:
public override Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
bool hasValue = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value);
return Task.CompletedTask;
}
... 和 CompletionProvider
:
public override Task ProvideCompletionsAsync(CompletionContext context)
{
bool hasValue = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value);
return Task.CompletedTask;
}
...还有 DiagnosticSuppressor
:
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
bool hasValue = context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value);
}
为了通过 Microsoft.CodeAnalysis.Testing 进行测试,您可以通过 AnalyzerTest<TVerifier>
的 SolutionState TestState
属性 的 ProjectState.AnalyzerConfigFiles
添加全局分析器配置选项:
string config = $"is_global = true{Environment.NewLine}build_property.MyAnalyzer_MyProperty = {true}";
analyzerTest.TestState.AnalyzerConfigFiles.Add(("/.globalconfig", config));
上面我描述了自定义 MSBuild 属性 MyAnalyzer_MyProperty
的用法,但当然它也适用于众所周知的 ImplicitUsings
属性。
我正在构建 roslyn analyzer/code 修复程序,但我不想访问 MSBuild 属性和元数据(来自 Directory.build.props 和 .csproj)以了解如何应用代码修复。我只找到了在源代码生成器中执行此操作的文档,但在分析器中找不到。
更具体地说,我想知道项目是否配置为使用新的 ImplicitUsings,但如果能访问所有内容也会很有用。
还有我们有什么办法让所有的项目全局使用吗?
并且使用新的 Microsoft.CodeAnalysis.Testing 如何添加 MSBuild 属性 以便我可以实际测试它?
此致。
在 DiagnosticAnalyzer
s 中访问 MSBuild 属性和元数据实际上与在 ISourceGenerator
s/[=20 中读取和测试它们的方式非常相似=]s,因为 Source Generators 在技术上也是 .NET 分析器 。
我假设您提到的文档是 Source Generators Cookbook.
首先,我们需要使 MSBuild 属性 可用于分析器的全局分析器配置选项:
<Project>
<ItemGroup>
<CompilerVisibleProperty Include="MyAnalyzer_MyProperty" />
</ItemGroup>
</Project>
然后,我们可以从 AnalyzerConfigOptionsProvider 的 GlobalOptions
中读取 属性 的值。您会在您选择的 AnalysisContext.Register*
方法的参数中找到它,您在覆盖的 DiagnosticAnalyzer.Initialize(AnalysisContext)
方法中使用该方法。
例如 RegisterCompilationAction
:
bool isEnabled = false;
if (compilationAnalysisContext.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value))
{
isEnabled = value.Equals("true", StringComparison.OrdinalIgnoreCase) || value.Equals("enable", StringComparison.OrdinalIgnoreCase);
}
ImmutableDictionary<string, string?>.Builder properties = ImmutableDictionary.CreateBuilder<string, string?>();
if (isEnabled)
{
properties.Add("IsEnabled", value);
}
var diagnostic = Diagnostic.Create(Rule, location, properties.ToImmutable());
compilationAnalysisContext.ReportDiagnostic(diagnostic);
CodeFixProvider
的 CodeFixContext
没有专用的 AnalyzerOptions Options
属性,但您可以通过 Diagnostic.Properties
:
foreach (Diagnostic diagnostic in context.Diagnostics)
{
if (diagnostic.Properties.TryGetValue("IsEnabled", out string? value))
{
var action = CodeAction.Create(Title, cancellationToken => OnCreateChangedDocument(context.Document, cancellationToken), diagnostic.Id);
context.RegisterCodeFix(action, diagnostic);
}
}
... 或者,我刚刚在撰写此答案时发现的,通过 CodeFixContext.Document.Project.AnalyzerOptions
访问 AnalyzerConfigOptionsProvider
。这适用于任何可用的 Document
(或 Project
):
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
bool hasValue = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value);
}
此外,它适用于 CodeRefactoringProvider
:
public override Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
bool hasValue = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value);
return Task.CompletedTask;
}
... 和 CompletionProvider
:
public override Task ProvideCompletionsAsync(CompletionContext context)
{
bool hasValue = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value);
return Task.CompletedTask;
}
...还有 DiagnosticSuppressor
:
public override void ReportSuppressions(SuppressionAnalysisContext context)
{
bool hasValue = context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue("build_property.MyAnalyzer_MyProperty", out string? value);
}
为了通过 Microsoft.CodeAnalysis.Testing 进行测试,您可以通过 AnalyzerTest<TVerifier>
的 SolutionState TestState
属性 的 ProjectState.AnalyzerConfigFiles
添加全局分析器配置选项:
string config = $"is_global = true{Environment.NewLine}build_property.MyAnalyzer_MyProperty = {true}";
analyzerTest.TestState.AnalyzerConfigFiles.Add(("/.globalconfig", config));
上面我描述了自定义 MSBuild 属性 MyAnalyzer_MyProperty
的用法,但当然它也适用于众所周知的 ImplicitUsings
属性。