Open16

CheckStyleを導入する

netainetai

CheckStyleとは

https://checkstyle.sourceforge.io/

  • Javaのための静的解析ツール
  • 定めたルールに従わないコードを警告してくれる
  • Sun(Oracle)やGoogleのコーディングスタイルを設定できる
  • XMLを書き換えて自由に設定変更できる
netainetai

導入のモチベーション

Wordで書かれたコーディング規約だけが存在しているので、それを可能な限りCheckStyleに落とし込む

netainetai

CheckStyleXMLの基本

  • CheckStyleの設定はXMLドキュメントを通して行う。
  • Eclipseアドオン上のGUIからでも設定できるが、使いづらいので確認用にとどめたほうが良い

設定ファイルの例

<module name="Checker">
    <module name="JavadocPackage"/>
    <module name="TreeWalker">
        <module name="AvoidStarImport"/>
        <module name="ConstantName"/>
        <module name="EmptyBlock"/>
    </module>
</module>

簡単なまとめ

  • チェック(設定の単位)はすべてmodule要素のnameによって指定される
  • すべてのチェックは既定の親子関係を持ち、module要素のヒエラルキーはそれに従う
  • すべてのチェックはCheckerの子孫要素である
  • 多くのチェックがTreeWalkerの子要素である
    • TreeWalkerがソースを解析するので、逆に言えば、TreeWalkerが親でないチェックは簡易なものが多い(行数確認など)
netainetai

property

各チェックはデフォルトのままでも使えるようになっているが、module要素にはpropertyを追加することで、細かい設定変更が可能になる

- <module name="MethodLength" />
+ <module name="MethodLength">
+    <property name="max" value="60"/>
+ </module>

MethodLengthはその名の通りメソッドの行数に関するチェックで、デフォルトでは150行を超えると警告が出るようになっている。
これにpropertyを追加することで、最大行数を変更するなどの細かい調整が可能になる

severity

<module name="Translation">
    <property name="severity" value="warning"/>
</module>
  • すべてのチェックはseverityプロパティを設定できる
  • 警告の度合いに関するものであり、デフォルトはerror
    • とドキュメントには書いてあるが、Checker以外のデフォルトはinheritなので、Checkerモジュールのseverityを変更することで一括変更も可能
netainetai

message

propertyと同様に設定できる項目としてmessageがある

<module name="MemberName">
    <property name="format" value="^m[a-zA-Z0-9]*$"/>
    <message key="name.invalidPattern"
             value="Member ''{0}'' must start with a lowercase ''m'' (checked pattern ''{1}'')."
             />
</module>
  • 警告メッセージはデフォルトでも設定されている(ちゃんと日本語もある)が、message要素で変更できる
  • デフォルトメッセージで使われるプレースホルダーはカスタムメッセージでも使える
  • ドキュメントの各チェックのページからerrorkeyのリンクを踏むと、デフォルトメッセージの構成が確認できる
    • 一部リンク切れしており確認が面倒…
netainetai

使用したチェックの紹介

FallThrough

JavaのSwitch文は、Breakを記述しないと下の節も実行してしまう。

switch (i){
case 0: // i==0でこれを実行した後
    i++; 
case 1: // この節も実行してしまう
    i++;
    break;
}

これはFallThrough(フォールスルー)と呼ばれており、意図的にこれを活かすコードもあるが、挙動が直感的でなくバグの温床となり得る。

このような記述をさせないのがFallThroughチェック

<module name="FallThrough"/>
  • breakのないSwitch文を警告してくれるようになる
  • このチェックはデフォルトだと、最後のcase節にbreakが付いているかはチェックしない
    • <property name="checkLastCaseGroup" value="true"/>を追加することで、チェックするように変更できる
netainetai

MissingSwitchDefault

<module name="MissingSwitchDefault"/>
  • Switch文にdefaultがないことを警告してくれるようになる
  • default節をつける意味もドキュメントで言及されている

Rationale: It's usually a good idea to introduce a default case in every switch statement. Even if the developer is sure that all currently possible cases are covered, this should be expressed in the default branch, e.g. by using an assertion. This way the code is protected against later changes, e.g. introduction of new types in an enumeration type.

netainetai

DefaultComesLast

<module name="DefaultComesLast"/>
  • default節が最後に来てないと警告する
  • 一応文法上はdefaultが最後以外に挿入されててもOKらしい(知らなかった)
    • 流石にcase, default, case, case... みたいな順序で書く人はいないと思うが、もしかしたらdefaultを最初に置く人はいるかも知れない
netainetai

JavadocMethod

メソッドのJavadocに適切なアノテーションが設定されてない場合、警告する

  • 引数のあるメソッドには@paramを付ける
  • 返り値のあるメソッドには@returnを付ける
  • 例外を投げるメソッドには@throwsを付ける
<module name="JavadocMethod"/>

デフォルトだと@overrideアノテーションのあるメソッドを無視する

netainetai

識別子名系のチェック

使用したのは以下

  • ConstantName
  • MethodName
  • PackageName
  • TypeName

使い方はほとんど同じなのでまとめる

<module name="MethodName"/>
  • 基本的にはデフォルトで使える
  • 先頭が大文字/小文字、記号使用不可などが正規表現でチェックされる

正規表現を使って基準を変更できる

<module name="PackageName">
    <!-- 小文字と数字のみ(デフォルトでは大文字も許可される) -->
    <property name="format"
              value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
</module>
netainetai

A.equals(B)のAに文字列を置く

<module name="EqualsAvoidNull"/>
  • Javaのぬるぽ避けテクニックとして、equalsの左に文字列を置くようにするというものがある
  • EqualsAvoidNullは、このテクニックに則っていないものをチェックする
netainetai

インデント

<module name="Indentation"/>
  • Indentationはインデントに関するチェックを導入する
  • propertyが多いので紹介する(単位は半角スペース)
    • basicOffset: 文頭のインデント、デフォルトは4
    • braceAdjustment: 中括弧のインデント、デフォルトは0
    • caseIndent: switch文に対するcase節のインデント、デフォルトは4
    • throwsIndent: throwsのインデント、デフォルトは4
    • arrayInitIndent: 配列初期化時のインデント、デフォルトは4
    • lineWrappingIndentation: 改行インデント、デフォルトは4
    • forceStrictCondition: trueだと改行インデントはlineWrappingIndentationの値とぴったり同じでないといけなくなる。falseならlineWrappingIndentationの値と同じか大きければOK
<module name="TreeWalker">
    <property name="tabWidth" value="4"/>
</module>
  • スペースでなくTab派の場合、TreeWalkerのpropertyのtabWidthを導入しておく
netainetai

Length系

<module name="FileLength">
    <property name="max" value="1000"/>
</module>
<module name = TreeWalker>
    <module name="LineLength">
        <property name="max" value="100"/>
    </module>
    <module name="MethodLength">
        <property name="max" value="100"/>
    </module>
</module>
  • ファイルやメソッドの行数、一行の長さをチェックすることができる
  • この内、FileLengthはTreeWalkerではなくCheckerが親になることに注意
netainetai

TODOチェック

<module name="TodoComment"/>
  • TODO:コメントは(少なくとも)Eclipseでは強調表示されるため、使用者が多い
    • 一方で、書いた事自体を忘れがち
  • TodoCommentを導入すれば、リマインダのような役割を果たしてくれる
  • 自分がTODO:コメント使わない場合でも、過去のTODOが残っていないかの検査に役立つはず
  • <property name="format" value="正規表現">を差し込めばTODO:以外のアノテーションコメント(例えばこの記事)もチェックしてくれる