我们有一些代码,根据一些标准(包括环境名称)命名基础设施构造。这是一个简化的示例:
enum AppEnvironment
{
Dev,
Staging,
Production
}
static class AppEnvironmentExtensions
{
static string AsString(this AppEnvironment env) => env switch
{
AppEnvironment.Dev => "dev",
AppEnvironment.Staging => "stage",
AppEnvironment.Production => "prod",
_ => throw new InvalidOperationException("Invalid environment")
};
}
void BuildConstruct(string appName, AppEnvironment env)
{
string constructName = $"{env}-{appName}"; // ← mistake
// do infra stuff
}
这种情况下的问题是,当实际要求是将枚举成员解析为不同的字符串时,很容易意外地让底层枚举的
ToString
方法在字符串插值期间被调用(在这种情况下,我们的 AsString
方法) )。这可能会导致很难追踪的破损。我知道字符串常量不会有这个问题,但由于其他原因,这些常量并不理想。
设计时解决方案是当代码尝试使用此枚举的
ToString
方法时显示编译器警告。现在,开发人员需要明确决定是使用不同的方法还是留下 #pragma warning disable
声明来允许它。它需要足够聪明才能知道何时间接调用 ToString
,例如在字符串插值或 String.Format
中。
这在 Roslyn 分析器中实现起来很简单吗?
你可以尝试这样的事情:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Immutable;
using System.Linq;
namespace Analyzer
{
[Generator]
internal class AnalyzerInit : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
Analyzer analyzer = new Analyzer();
analyzer.Initialize(context);
}
}
internal class Analyzer : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var rules = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => IsSyntaxTargetForGeneration(s),
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx))
.Where(static m => m is not null);
var compilation = context.CompilationProvider.Combine(rules.Collect());
context.RegisterSourceOutput(compilation, static (spc, source) => Execute(source.Left, source.Right, spc));
}
private static bool IsSyntaxTargetForGeneration(SyntaxNode node)
{
return node is EnumDeclarationSyntax e && e.Parent is LiteralExpressionSyntax l; //This might have to be specified and i cant remember if it would be called an EnumDeclarationSyntax
}
private static EnumDeclarationSyntax GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
{
var compilationUnitSyntax = (EnumDeclarationSyntax)context.Node;
return compilationUnitSyntax;
}
public static void Execute(Compilation compilation, ImmutableArray<EnumDeclarationSyntax> enums, SourceProductionContext context)
{
foreach (var enumDeclaration in enums)
{
var semanticModel = compilation.GetSemanticModel(enumDeclaration.SyntaxTree);
var sourceSymbol = (INamedTypeSymbol)semanticModel.GetTypeInfo(enumDeclaration).Type;
if (sourceSymbol.MetadataName == "AppEnvironment")
{
context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("ErrorCode", "/*Title*/AppEnvironment was refered to from Literaltring", $"/*Message*/You are not allowed to do this", "/*Category*/NotAllowedCategory", DiagnosticSeverity.Error, true), enumDeclaration.GetLocation()));
}
}
}
}
}
将此代码添加到新项目中的类中。
创建项目后,您可以从需要检查此项目的项目中引用它。
构建时,诊断将被报告为构建错误。
没有测试过,但据我记得,类似的东西会起作用。
之后您可以在规则中添加任何您想要的例外情况。
PS:我知道 Visual Studio 有一些工具或扩展,您可以安装和/或使用它来实时查看不同语法节点的调用方式,但我不记得它的名称了。尝试在 YouTube 上搜索 Roslyn 教程。