0
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?

More than 1 year has passed since last update.

Checkstyleでカスタムチェック

Last updated at Posted at 2021-07-01

カスタムなCheckstyleを使おうとして、いくつかのページを見たが、
私のケースと少し違って、少々調べる時間が必要になったので、メモを残す。

とはいえ、既によくまとまってるページがあったので、
基本的にはそのページとJava docや公式情報を見れば十分だった。

ちなみに私の場合はTokenTypesの情報はIDE経由で見た。
(内容は同じ)

Checkstyleでカスタムチェックをやろうとした動機

元々SpringBootTestを使ってCIでアノテーションチェックを行っていたが、

  • 処理が重い。
  • Gradleでexcludeを使ってる所為で例外処理を書く必要がある。

などの課題があった。
静的解析でなんとかならないかと思って調べたところ、
既に導入済みのcheckstyleでなんとかなりそうだったので、今回調べたという経緯である。

Gradleのマルチプロジェクト

私の場合、既にあるマルチプロジェクトの中のサブフォルダの中に
カスタムcheckstyleのプロジェクトを作成した。
このあたり↓の記事を参考にした。

build.gradle
allprojects {
  // 省略
  dependencies {
    checkstyle 'com.puppycrawl.tools:checkstyle:8.1'
    checkstyle project(':helper:custom-checkstyle')  
  }
  checkstle {
    // checkstyleの設定
  }
  // 省略
}

このやり方だと自身のプロジェクトもcheckstyleの対象になるので
ちゃんと動くか懸念があったが、特に問題なかった。
恐らくビルドの実行と、checkstyleの実行が別のためだと思われる。

ちなみにカスタムチェックを実装するモジュールの方のbuild.gradleは
こんな感じで継承するclassを含むパッケージを導入する。

./helper/build.gradle
plugins {
    id 'java'
}
repositories {
    mavenCentral()
}
dependencies {
    compile group: 'com.puppycrawl.tools', name: 'checkstyle', version: '8.1'
}

カスタムチェックの実装

実装するには以下を継承したクラスを作成し、

package com.sample;

import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;

public class CustomCheck extends AbstractCheck {
    String annotation;
    public void setAnnotation(String annotation) { this.annotation = annotation; }

    @Override
    public int[] getDefaultTokens() { return this.getRequiredTokens(); }
    @Override
    public int[] getAcceptableTokens() { return this.getRequiredTokens(); }
    @Override
    public int[] getRequiredTokens() {
        return new int[]{TokenTypes.METHOD_DEF};
    }
    // チェック処理の実装
    @Override
    public void visitToken(DetailAST ast) {
        // 前部でMETHODD_DEFを指定するとここでMETHOD_DEFのASTが渡される
    }
}

checkstyle.xmlで作成したクラスを指定すれば良い。

checkstyle.xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
        "https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
  <module name="TreeWalker">
    <module name="com.sample.CustomCheck">
        <property name="annotation" value="Transactional"/>
    </module>
  </module>
</module>
  • 字句種類は DetailAst#getType() で、字句内容は DetailAst#getText() で取得する。
  • NGのものがあればlog()を使う。

logは以下のように該当箇所とエラーメッセージを指定する。

log(ast.getLineNo(), "NG message")

あるいはxmlでメッセージやフォーマットを用意しておく方法もある。
(詳しくはAbstractCheckのjava docを参照)

また、私が試した際は標準出力などへの結果は表示されなかったので、
デバッグでcheckMainのタスクなどを実行するか、
logメソッドでprintデバッグを行った。

DetailAST

ASTの各ノードの扱い方は DetailAST や TokenType などのjava docに詳しく書いてある。

  • DetailAST#findFirstToken を使って子から特定のTokenTypeを検索。
  • modifiersのようにpublic/private/static/アノテーションなどがあるものは、
    以下のようにして検索できる。
for (DetailAST child = ast.getFirstChild();
     child != null;
     child = child.getNextSibling()) {
  // ループ内の処理
}

ASTの構造は以下を参考に。
例えばメソッド定義の構造であれば以下を見れば良い。

0
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
0
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?