如何在使用 C# 枚举的 ToString 方法时生成编译器警告

问题描述 投票:0回答:1

我们有一些代码,根据一些标准(包括环境名称)命名基础设施构造。这是一个简化的示例:

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 分析器中实现起来很简单吗?

c# enums roslyn-code-analysis
1个回答
0
投票

你可以尝试这样的事情:

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 教程。

© www.soinside.com 2019 - 2024. All rights reserved.