1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ぽんぬの個人的Advent Calendar 2023

Day 23

Roslyn APIを使ってStyleCop.Analyzersのルールを拡張してみる

Posted at

事前準備

環境

StyleCopAnalyzersの開発を行うには、以下のものが必要です。

  • C# 8.0 以上
  • .NET 5 以上
  • VisualStudio 2022 以上

リポジトリ

以下のStyleCopAnalyzersの公式リポジトリをローカルにクローンし、VisualStudio 2022でプロジェクトを開きます。

StyleCopAnalyzersのルールについて

StyleCopAnalyzersには、以下のようなルール既にあります。

Special Rules(特別ルール) (SA0000~)

回避策、構成エラーなどの特別な機能を提供するルール。

Spacing Rules(間隔の規則) (SA1000~)

コード内のキーワードと記号の間隔要件を強制するルール。

Readability Rules(可読性ルール) (SA1100~)

コードが適切にフォーマットされ、読みやすいことを保証するルール。

Ordering Rules(発注ルール) (SA1200~)

コードの内容に標準の順序付けスキームを適用するルール。

Naming Rules(命名規則) (SA1300~)

メンバー、型、および変数の名前付け要件を強制する規則。

Maintainability Rules(保守容易性ルール) (SA1400~)

コードの保守性を向上させるルール。

Layout Rules(レイアウトルール) (SA1500~)

コードのレイアウトと行間を強制するルール。

Documentation Rules(ドキュメンテーションルール) (SA1600~)

コードドキュメントの内容とフォーマットを検証するルール。

Alternative Rules(代替ルール) (SX0000~)

既定の StyleCop の動作に非標準の拡張機能を提供するルール。

開発

今回は、Layout Rulesに「三項演算子をネストしてはならない」と言うルールを追加していこうと思います。
適当なファイル(SA1520UseBracesConsistently.cs)辺りを複製し、名前をSX1580TernaryOperatorsNotBeNested.csに変更します。

クラスを定義している部分を、以下のソース

ベースのコード

新規でルールを作る際は、以下のような状態がベースとなります。
ここに肉付けしていきます。

StyleCop.Analyzers\LayoutRules\SX1580TernaryOperatorsNotBeNested.cs

[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class SX1580TernaryOperatorsNotBeNested : DiagnosticAnalyzer
{
    /// <summary>
    /// The ID for diagnostics produced by the <see cref="SX1580TernaryOperatorsNotBeNested"/> analyzer.
    /// </summary>
    public const string DiagnosticId = "SX1580";

    private static readonly LocalizableString Title = new LocalizableResourceString(nameof(LayoutResources.SX1580Title), LayoutResources.ResourceManager, typeof(LayoutResources));
    private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(LayoutResources.SX1580MessageFormat), LayoutResources.ResourceManager, typeof(LayoutResources));
    private static readonly LocalizableString Description = new LocalizableResourceString(nameof(LayoutResources.SX1580Description), LayoutResources.ResourceManager, typeof(LayoutResources));

    private static readonly DiagnosticDescriptor Descriptor =
        new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.LayoutRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description);

    /// <inheritdoc/>
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Descriptor);

    /// <inheritdoc/>
    public override void Initialize(AnalysisContext context)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();
    }
}

それぞれの変数の説明です。

  • DiagnosticId
    • 各ルールを識別するためのIDです。重複不可。
  • Title
    • ルールのタイトルです。
    • 後述するリソースファイルからメッセージを取得します。
  • MessageFormat
    • ルールの本文です。実際にアナライザの指摘部分に表示される部分になります。
    • 後述するリソースファイルからメッセージを取得します。
  • Description
    • ルールの説明です。
    • 後述するリソースファイルからメッセージを取得します。
  • Descriptor
    • 上記のIDやメッセージ、ルールカテゴリなどをまとめたものです。
    • 基本変更不要です。
  • SupportedDiagnostics
    • DescriptorImmutableArray型に変換して、外から参照出来るようにします。
    • 基本変更不要です。

処理を追加する

ベースのコードに以下の部分を追加します。
以下では、CheckChildExpressionメソッドを追加し、三項演算子がネストしている事を検知する処理を記載しています。

引数のExpressionSyntax型の引数childExpressionに、構文やトークンの情報※1が入っている為、それが三項演算子かどうか判定しています。
構文が三項演算子と判定された後に、再度三項演算子と判定されれば、ネストしていると断定することが出来ます。

最後に、Initializeメソッドで先ほど追加したCheckChildExpressionをコンテキストに登録します。
こうすることで、アナライザー実行時に分析を行ってくれます。

StyleCop.Analyzers\LayoutRules\SX1580TernaryOperatorsNotBeNested.cs

[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class SX1580TernaryOperatorsNotBeNested : DiagnosticAnalyzer
{
    /// <summary>
    /// The ID for diagnostics produced by the <see cref="SX1580TernaryOperatorsNotBeNested"/> analyzer.
    /// </summary>
    public const string DiagnosticId = "SX1580";

    private static readonly LocalizableString Title = new LocalizableResourceString(nameof(LayoutResources.SX1580Title), LayoutResources.ResourceManager, typeof(LayoutResources));
    private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(LayoutResources.SX1580MessageFormat), LayoutResources.ResourceManager, typeof(LayoutResources));
    private static readonly LocalizableString Description = new LocalizableResourceString(nameof(LayoutResources.SX1580Description), LayoutResources.ResourceManager, typeof(LayoutResources));

    private static readonly DiagnosticDescriptor Descriptor =
        new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.LayoutRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description);

    /// <inheritdoc/>
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Descriptor);

    /// <inheritdoc/>
    public override void Initialize(AnalysisContext context)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();

+       context.RegisterSyntaxNodeAction(ctx => CheckChildExpression(ctx, ((ConditionalExpressionSyntax)ctx.Node)), SyntaxKind.ConditionalExpression);
    }

+   private static void CheckChildExpression(SyntaxNodeAnalysisContext context, ExpressionSyntax childExpression)
+   {
+       if (childExpression is ConditionalExpressionSyntax child)
+       {
+           if (child.Parent is ConditionalExpressionSyntax)
+           {
+               context.ReportDiagnostic(Diagnostic.Create(Descriptor, childExpression.GetLocation()));
+           }
+       }

        return;
    }
}

※1 構文やトークンの情報について
VisualStudioのRoslyn Syntax Visualizerと言うツールを使用すると、以下のようにコードが木構造で分析された結果を表示することが出来ます。

image-2.png

メッセージの追加

リソースファイルにメッセージを追加します。
以下はコードで表現していますが、基本的にGUIで編集することが多いです。
MessageFormatでは「"if分を{0}回以上ネストする事は出来ません"」のように記述することで、後で値を受けこむことが可能です。

StyleCop.Analyzers\LayoutRules\LayoutResources.ja-JP.resx
.....
<data name="SX1580Description" xml:space="preserve">
  <value>三項演算子をネストすることは出来ません</value>
</data>
<data name="SX1580MessageFormat" xml:space="preserve">
  <value>三項演算子をネストすることは出来ません</value>
</data>
<data name="SX1580Title" xml:space="preserve">
  <value>三項演算子をネストすることは出来ません</value>
</data>
.....

アナライザーの出力

ターゲットフレームワークの設定

アナライザーの作成が終わったら、アナライザーを動かす為のターゲットフレームワークを指定します。
プロジェクト -> 右クリック -> プロパティ -> アプリケーション -> 全般 -> ターゲットフレームワーク

今回は.NET Framework 4.7.2向けに出力したいので、net472の様に指定します。

image.png

dllファイルの生成

設定が終わったら、プロジェクトを'Relese'でビルドします。
そうすると\StyleCop.Analyzers\bin\Release\net472\の配下にStyleCop.Analyzers.dllというファイルが生成されます。

アナライザーの適応

出力が終わったら、適応したいプロジェクトをVisualStudioで開き、
プロジェクト -> 参照 -> アナライザー -> 右クリック -> アナライザーの追加で、先ほど出力したStyleCop.Analyzers.dllを参照に加えます。

image-1.png

ここまで来たら、もう少しです。

プロジェクト -> 右クリック -> プロパティ -> コード分析 -> アクティブな規則 -> 構成でStyleCopの使いたいルールにチェックを入れます。(SA~やSX~がStyleCopのルールになります。)
ルールを保存すると、*.rulesetをファイルが生成される為、エディタ等で編集することも可能です。
※VisualStudio 2022からは上記の方法は廃止され、EditorConfigによるルール設定に切り替わっています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?