Scalastyle provides functionality to enforce style rules and best practices in Scala, similar to jslint, FXCop, or PMD. Setting it up is straightforward.
You can follow along here:
https://github.com/jieyangh/scalatra-sample-API with git hash 5d40618622e79835e870fac1533a90bbf9694dc3
First, we will modify plugins.sbt to add the scalastyle plugin and a resolver to help sbt find the plugin:
resolvers += "sonatype-releases" at "https://oss.sonatype.org/content/repositories/releases/" addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.6.0")
Next we add the scalastyle project settings to the project settings. The scalastyle default project settings will automatically cause the build task currently being executed to fail if scalastyle fails (scalastyleFailOnError):
lazy val project = Project ( "sampleAPI", file("."), settings = Defaults.defaultSettings ++ Seq(webSettings :_*) ++ Seq(org.scalastyle.sbt.ScalastylePlugin.projectSettings :_*) ++ Seq( libraryDependencies ++= Seq( "org.scalatra" %% "scalatra" % "2.2.2", "org.eclipse.jetty" % "jetty-webapp" % "8.1.7.v20120910" % "container,compile", "org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016", "ch.qos.logback" % "logback-classic" % "1.0.1", "org.scalatra" %% "scalatra-scalatest" % "2.2.2" % "test" ) ) )
For convenience, we can define our own custom build task “full” in build.sbt that does a clean, updates the packages, runs the tests and then runs scalastyle:
addCommandAlias("full", ";clean ;compile ;test ;scalastyle")
Before we run scalastyle we need to generate the config by first running:
Now when we run ./sbt full we will see the scalastyle errors. You can see that it enforces some Scala best practices:
[info] scalastyle using config /Users/jiehu/scalatra-sample-API/scalastyle-config.xml [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/sampleApi/config/VersionInfo.scala:1: Header does not match expected text [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/sampleApi/config/VersionInfo.scala:37:0: Whitespace at end of line [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/sampleApi/config/VersionInfo.scala:27:7: Avoid using return [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/sampleApi/config/VersionInfo.scala:32:30: Avoid using return [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/sampleApi/config/VersionInfo.scala:16:28: Avoid using null [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/sampleApi/config/VersionInfo.scala: File must end with newline character [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/sampleApi/controllers/GreetingController.scala:1: Header does not match expected text [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/sampleApi/controllers/GreetingController.scala: File must end with newline character [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/sampleApi/controllers/HealthCheckController.scala:1: Header does not match expected text [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/ScalatraBootstrap.scala:1: Header does not match expected text [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/ScalatraBootstrap.scala:12:1: Whitespace at end of line [warn] /Users/jiehu/scalatra-sample-API/src/main/scala/ScalatraBootstrap.scala: File must end with newline character
We can look up the rules here. I disabled the “Header does not match expected text” which is configured in scalastyle-config.xml file by setting enabled to false. This just checks if the predefined header appears at the start of every file. Since this is a simple tutorial project, there is no need for any such headers to exist. This makes the rule un-necessary.
<check level="warning" class="org.scalastyle.file.HeaderMatchesChecker" enabled="false">
You can also turn off scalastyle in sections of code by putting them between //scalastyle:off and //scalastyle:on
It is best practice to fully specify the rule id being turned off //scalastyle:off magic.number This way it will only suppress that specific warning and not any others that may exist.
The rest of the checkin is just fixing the miscellaneous scalastyle errors. Once all of them have been fixed, the build succeeds!
NOTE: I had also tried using findbugs as well but found it didn’t play well with Scala. Findbugs works on Java bytecode. However when Scala compiles down to Java bytecode, it often results in Findbugs errors that aren’t really errors. For example, it will report that class names aren’t capitalized when in actuality they are, its just that traits apparently have lowercase class names in the java byte code.