読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

StyleCop でカスタムスタイルを入れてみる

.NET Tips OSS C#

http://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=sourceanalysis&ReleaseId=1425
ここで StyleCop と一緒に配布されてる SDK を落として解凍、、、、、はしなくていいかも知れない。
だって chm ドキュメントが1個転がってるだけだ(手順が書いてある)。
それでもAPIドキュメント付なので、あったほうが断然よい。

ちなみにここの説明がわかりやすかったのでメモ
http://www.dotnetspark.com/kb/500-c-sharp-code-reviews-using-stylecop.aspx

.NET Library プロジェクトを作成する

特に特記するものなし。普通に作ってよい。
ちなみに。.NET 3.5 で作って普通に動く。

Microsoft.StyleCop , Microsoft.StyleCop.CSharp に参照通す

StyleCop インストールディレクトリにファイルがあるはずなので参照する。

クラスを作る

論よりRunに近い何か。コメントの書き方で 3 つほど指摘されるが力尽きた。
MyCustomRules.cs

namespace CustomStyle
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Text.RegularExpressions;
    using Microsoft.StyleCop;
    using Microsoft.StyleCop.CSharp;

    /// <summary>
    /// サンプルのカスタムルールを作成します。
    /// このカスタムルールでは、プライベート変数の命名規則として、
    /// 小文字、もしくはアンダースコアを強制させます。
    /// また、空のブロック要素を禁止します。
    /// </summary>
    [SourceAnalyzer(typeof(CsParser))]
    public class MyCustomRules : SourceAnalyzer
    {
        /// <summary>
        /// オーバーライドされます。
        /// StyleCop のカスタムルールでは、このメソッドがエントリポイントとなります。
        /// </summary>
        /// <param name="document">
        /// パースされたドキュメント要素を取得します。
        /// ドキュメント要素は CsDocument 型で渡されます。
        /// </param>
        public override void AnalyzeDocument(CodeDocument document)
        {
            CsDocument csharpDocument = (CsDocument)document;
            if (csharpDocument.RootElement != null && !csharpDocument.RootElement.Generated)
            {
                csharpDocument.WalkDocument(
                    new CodeWalkerElementVisitor<object>(this.VisitElement),
                    new CodeWalkerStatementVisitor<object>(this.VisitStatement),
                    null);
                //// new CodeWalkerExpressionVisitor<object>(this.VisitExpression));
            }
        }

        /// <summary>
        /// private な変数で、小文字で始まらない or _ で始まらない変数は認めません。
        /// </summary>
        /// <param name="element">
        /// ターゲットとなる要素名を指定します。
        /// この要素を判定します。
        /// </param>
        /// <param name="parentElement">
        /// ターゲットとなる要素を含む要素(ここではもっぱらクラス定義のブロック)をあらわします。
        /// 基本的に利用していません。
        /// </param>
        /// <param name="context">
        /// 追加パラメータとして渡されます。
        /// ぶっちゃけわかりません。
        /// </param>
        /// <returns>
        /// 解析を継続するかどうかを表します。
        /// 継続しない場合は false を返します。
        /// </returns>
        private bool VisitElement(CsElement element, CsElement parentElement, object context)
        {
            if (element.ElementType == ElementType.Field)
            {
                Regex reg = new Regex("^[a-z_]");
                if (!reg.IsMatch(element.Declaration.Name))
                {
                    this.AddViolation(element, element.LineNumber, "PrivateNameHaveToLowerOrUnderscore");
                }
            }

            return true;
        }

        /// <summary>
        /// メソッド以下のブロックにて、空っぽのループや分岐条件を警告します。
        /// このメソッドでは、要素とは別に、ステートメントのチェックを行います。
        /// </summary>
        /// <param name="statement">
        /// 検査対象のステートメントです。
        /// ブロックであるかどうか、また、子要素数を確認します。
        /// </param>
        /// <param name="parentExpression">
        /// このステートメントを含む Expression だそうですが、
        /// ぶっちゃけなんのこっちゃわかりません。使ってりゃ把握するんじゃないかと。
        /// </param>
        /// <param name="parentStatement"></param>
        /// <param name="parentElement"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private bool VisitStatement(Statement statement, Expression parentExpression, Statement parentStatement, CsElement parentElement, object context)
        {
            if (statement.StatementType == StatementType.Block && statement.ChildStatements.Count == 0)
            {
                // {} 空のブロックは禁止
                this.AddViolation(parentElement, statement.LineNumber, "BlockStatementsShouldNotBeEmpty");
            }

            return true;
        }
    }
}
XML リソースを作る

このリソースは「埋め込まれたリソース」を指定すること。
MyCustomRules.xml

<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="MyCustomRules">
  <Description>
    private name and blank block check.
  </Description>
  <Rules>
    <RuleGroup Name="My Policy">
      
      <Rule Name="BlockStatementsShouldNotBeEmpty" CheckId="AZ1000">
        <Context>A block statement should always contain child statements.</Context>
        <Description>Validates that the code does not contain any empty block statements.</Description>
      </Rule>
      
      <Rule Name="PrivateNameHaveToLowerOrUnderscore" CheckId="AZ1001">
        <Context>Private name have to starts with lower case or underscore.</Context>
        <Description>Private name have to starts with lower case or underscore.</Description>
      </Rule>
      
    </RuleGroup>
  </Rules>
</SourceAnalyzer>
コンパイルしてデプロイする

コンパイルして、StyleCop インストールディレクトリにポイ。
これで勝手に読み込んで動く。素敵。