scalac にもっと警告してもらう


これは[Scala Advent Calendar 2014](http://qiita.com/advent-calendar/2014/scala) 6日目の記事です。


Scala はコンパイルが遅いことで有名ですが、コンパイルの嬉しいところは、実行前に沢山のプログラムミスやミスっぽいものをキャッチできることです。
C を書くときは -Wall -Wextra -Wpedantic あたりをつけるとたくさん警告だしてくれてありがたいですね。Scala の場合もたくさん警告出して欲しい。scalac 2.11.4 にある警告関連のオプションを見てみましょう。

### オプションの区分

とその前に、scalac オプションの区分についておさらいです。オプションは

* -X や -Y 以外から始まる普通のオプション
* -X から始まる advanced オプション
* -Y から始まる private オプション

に分類されています。それぞれ `scalac -help`, `scalac -X`, `scalac -Y` でヘルプを見ることが出来ます。
advanced や private の意味するところは正直良くわかっていませんが、普通のオプションより-Xのほうが、-Xより-Yのほうが変化が激しいようです。

### 普通の警告オプション

普通のオプションで警告関連のものは次の3つです。

* `-deprecation`: `@deprecated` なAPIが使われている箇所を警告します
* `-feature`: langauge feature の import が必要な箇所を警告します
* `-unchecked`: ヘルプによると Enable additional warnings where generated code depends on assumptions. とのことです（が、何のことだかよくわかってません…）

とりあえず、この3つは付けましょう。

### advanced 警告オプション

advanced な警告関連のオプションは `-Xlint` と `-Xfatal-warnings` です。

`-Xlint` は様々な警告を有効にします。有効になる警告は `scalac -Xlint:help` で一覧できます(scala 2.11.2 以降)。2.11.4 で実行してみるとこうなりました。

````
Enable or disable specific warnings
  adapted-args               Warn if an argument list is modified to match the receiver.
  nullary-unit               Warn when nullary methods return Unit.
  inaccessible               Warn about inaccessible types in method signatures.
  nullary-override           Warn when non-nullary `def f()' overrides nullary `def f'.
  infer-any                  Warn when a type argument is inferred to be `Any`.
  missing-interpolator       A string literal appears to be missing an interpolator id.
  doc-detached               A ScalaDoc comment appears to be detached from its element.
  private-shadow             A private field (or class parameter) shadows a superclass field.
  type-parameter-shadow      A local type parameter shadows a type already in scope.
  poly-implicit-overload     Parameterized overloaded implicit methods are not visible as view bounds.
  option-implicit            Option.apply used implicit view.
  delayedinit-select         Selecting member of DelayedInit
  by-name-right-associative  By-name parameter of right associative operator.
  package-object-classes     Class or object defined in package object.
  unsound-match              Pattern match may not be typesafe.
````

`-Xlint` とだけ指定すれば、これら全てが有効になります。とにかく一杯警告出して欲しいので `-Xlint` も指定しましょう。

`-Xfatal-warnings` は警告をエラーとして扱うオプションです。
個人的には付けるほうが好みですが、どうしても消せない警告があるときにコンパイルできずに困るかもしれません。とりあえず付けた状態で始めて、どうしようもなくなったら外すという運用が良いと思います。


### private 警告オプション

`scalac -Y 2>&1 | grep warn` するとこのようになります。

```
  -Yinline-warnings                       Emit inlining warnings. (Normally surpressed due to high volume)
  -Ywarn-adapted-args                     Warn if an argument list is modified to match the receiver.
  -Ywarn-dead-code                        Warn when dead code is identified.
  -Ywarn-inaccessible                     Warn about inaccessible types in method signatures.
  -Ywarn-infer-any                        Warn when a type argument is inferred to be `Any`.
  -Ywarn-nullary-override                 Warn when non-nullary `def f()' overrides nullary `def f'.
  -Ywarn-nullary-unit                     Warn when nullary methods return Unit.
  -Ywarn-numeric-widen                    Warn when numerics are widened.
  -Ywarn-unused                           Warn when local and private vals, vars, defs, and types are are unused.
  -Ywarn-unused-import                    Warn when imports are unused.
  -Ywarn-value-discard                    Warn when non-Unit expression results are unused.
```

いくつかは `-Xlint` とかぶっているようですね。
かぶっていないものは

* `-Ywarn-dead-code`
* `-Ywarn-numeric-widen`
* `-Ywarn-unused`
* `-Ywarn-unused-import`
* `-Ywarn-value-discard`

の5つです。

Scala のバージョンによってあったりなかったりすると思うので、利用できる場合には利用すると良いかと思います。

ちなみに `-Ywarn-unused-import` を指定すると、 `sbt console` で `import` するたびに警告されます。 `-Xfatal-warnings` と同時に指定している場合はエラーになってしまいます。
 `:paste` で import 文と使用箇所をまとめて貼り付けるか、console を使う時だけ `-Ywarn-unused-import` か `-Xfatal-warnings` のどちらかを外すかで回避します。
console で不便なことを差し引いても警告が出たほうがうれしいですね！


### sbt での指定方法

scalac コマンドを直接使う時はコマンドライン引数として指定しますが、sbt を使っている方が多いと思います。
sbt での指定方法は `build.sbt` に `scalacOptions ++= Seq(/* ここに警告オプション */)` です。

### まとめ

Scala 2.11.4 で沢山警告をだすには、次のように `build.sbt` に書きます。

```Scala:build.sbt
scalacOptions ++= Seq(
  "-deprecation",
  "-feature",
  "-unchecked",
  "-Xlint",
  "-Ywarn-dead-code",
  "-Ywarn-numeric-widen",
  "-Ywarn-unused",
  "-Ywarn-unused-import",
  "-Ywarn-value-discard"
  // 警告をエラーにする（お好みに応じて）
  , "-Xfatal-warnings"
)
```

諸般の事情で 2.11.4 を使っていない人も居ると思います。Scala のバージョンによって存在する警告オプション（特に `-Ywarn-なんとか` ）は異なるので、 `scalac -help`, `scalac -X`, `scalac -Y` を参照して存在する警告オプションを片っ端から有効にしましょう。


私たちが間違った方向に行くのをいつも諌めてくれる scalac 先生に感謝しましょう。