Kengo's blog

Technical articles about original projects, JVM, Static Analysis and JavaScript.

SpotBugsプラグイン実装方法2017

過去にFindBugsプラグインの実装方法について記事にしたとおり、FindBugsプラグインの実装には複雑なハックが必要でした。特にfindbugs.xmlやmessages.xmlなどのメタデータ管理が煩雑でした。

これがSpotBugs 3.1.0-RC3ではある程度楽になっているので、シンプルになった方法をここにまとめておきます。

プロジェクトの雛形を作る

archetypeがコミュニティから提供されるようになりました。これを使えばプラグイン開発の知識がなくてもすぐにプロジェクトを作成できます。

github.com

使い方は他のmaven archetypeと同じで、archetype:generate実行時にarchetypeのgroupId, artifactIdそしてversionを指定するだけです。現状バージョン0.1.0が最新なので、以下のコマンドを実行してください:

$ mvn archetype:generate \
    -DarchetypeArtifactId=spotbugs-archetype \
    -DarchetypeGroupId=com.github.spotbugs \
    -DarchetypeVersion=0.1.0

バグとして検出すべきケースを実装し、単体テストを回す

プロジェクト生成に成功すると、src/test/javaに2つのクラスが見つかるはずです。

これらを実際に解析して結果を確認しているテストケースもあります。

このテストケースは、SpotBugsが出しているtest-harnessモジュールを使っています*1SpotBugsRuleというクラスがキモで、これを使って実際の.classファイルを解析し結果を確認できます。assertion用のMatcherクラスも提供されているので、hamcrestを使ったテストも書きやすいです。

     @Rule
     public SpotBugsRule spotbugs = new SpotBugsRule();
     @Test
     public void testIssuesAreFound() {
         Path path = Paths.get("target/classes/my/company/AnalyzedClass.class")
         BugCollection bugCollection = spotbugs.performAnalysis(path);

         // There should only be exactly 1 issue of this type
         final BugInstanceMatcher bugTypeMatcher = new BugInstanceMatcherBuilder()
                 .bugType("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE").build();
         assertThat(bugCollection, containsExactly(bugTypeMatcher, 1));

         final BugInstanceMatcher bugInstanceMatcher = new BugInstanceMatcherBuilder()
                 .bugType("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
                 .inClass("AnalyzedClass")
                 .atLine(25)
                 .build();
         assertThat(bugCollection, hasItem(bugInstanceMatcher));
     }

以前はプラグインを実際に使ったテストをするためにはfindbugs-maven-pluginを使ってインテグレーションテストを回すしか方法がありませんでしたが、この手法ならより高速にテストを回せます。また test-driven-detectors4findbugsと違って公式なので、今後のSpotBugsの更新にも追随できるはずです*2

メタデータを作成する

まずbugrank.txtは現状必要ないようです。無くてもプラグインはビルドできます。そもそもBugRankという概念はこの論文でやこのページ紹介されている通り、SonarQubeのSeverityのようなもので、priority (confidence)とは全く別の概念です。が、果たしてそれがどの程度浸透しているのか……。SpotBugsが使うpriorityやpriority (confidence)のことは忘れてSonarQubeのSeverityだけ使うのが、運用も回しやすいと思います。

ので、全体的な情報を提供するfindbugs.xmlと、ユーザ向けメッセージを管理するmessages.xmlだけ作成すれば十分です。プロジェクトの雛形にはこれらが既に作成されているので、それを参考にコピペで要素を増やしてください。

番外編:SonarQubeプラグインを作成する

SonarQubeプラグインsonar-packaging-maven-plugin を使ってパッケージできます。sonar-findbugsと拙作findbugs-slf4jが参考になります。

キモはfindbugs-pluginバージョン3.5への依存を宣言することです。このバージョンはSpotBugs 3.1.0-RC2を使っているため、SpotBugsに依存したプラグインを書くには必須です。

なおsonar-findbugsバージョン3.5はpre-releaseバージョンで、まだSonarQubeのUpdate Centerでは配布されていません。使用する際はGitHubのリリースページからjarを落としてきて手動インストールする必要があります。

またSonarQube用のrule.xmlを生成する必要があるのですが、これは雑ですがMavenプラグインを作ったので使ってもらえればと思います。Groovy好きな方はsonar-findbugsのコードを参照しても良いでしょう。

まとめ

以上でSpotBugsプラグインを実装してSonarQubeも含めた運用を回すことができるはずです。 まだ各種親Detectorを学習する手間は必要ですが、5年前に比べてだいぶマシにはなりました。

*1:find-sec-bugsからコードを持ってきて作られたもの

*2:test-driven-detectors4findbugsはFindBugs 1.3.9〜2.0.2にしか対応していなかった