aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz.driver.sbt/Linting.scala
blob: 8a293b625ebf3f2118fb63b3d1f3f4d09bba4e0a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package xyz.driver.sbt

import com.lucidchart.sbt.scalafmt.ScalafmtCorePlugin.autoImport.{scalafmtConfig, _}
import com.lucidchart.sbt.scalafmt.ScalafmtPlugin
import org.scalastyle.sbt.ScalastylePlugin
import org.scalastyle.sbt.ScalastylePlugin.autoImport.{scalastyle, scalastyleConfig}
import sbt.Keys._
import sbt._

import scala.collection.JavaConverters._

/** Enforces common formatting and catches compiler warnings. */
object Linting extends AutoPlugin {

  override def requires = ScalafmtPlugin && ScalastylePlugin
  override def trigger  = allRequirements

  lazy val formatSettings: Seq[Def.Setting[_]] = Seq(
    scalafmtConfig := {
      val packaged = getClass.getClassLoader.getResourceAsStream("scalafmt.conf")
      val out      = file(".scalafmt.conf")
      IO.write(out, IO.readBytes(packaged))
      out
    },
    scalafmtTestOnCompile in Test := true
  )

  lazy val scalastyleSettings: Seq[Def.Setting[_]] = Seq(
    scalastyleConfig := {
      val stream = getClass.getClassLoader.getResourceAsStream("scalastyle-config.xml")
      val out    = file(".scalastyle-config.xml")
      IO.write(out, IO.readBytes(stream))
      out
    },
    test in Test := (test in Test).dependsOn((scalastyle in Test).toTask("")).value
  )

  lazy val scalacSettings: Seq[Def.Setting[_]] = Seq(
    scalacOptions in Compile ++= Seq(
      "-language:higherKinds",
      "-language:implicitConversions",
      "-language:postfixOps",
      "-language:reflectiveCalls", // TODO this should be discouraged
      "-unchecked",
      "-deprecation",
      "-feature",
      "-encoding",
      "utf8",
      "-Xlint:_,-unused,-missing-interpolator",
      "-Ywarn-numeric-widen",
      "-Ywarn-dead-code",
      "-Ywarn-unused:_,-explicits,-implicits"
    ),
    // Currently, scalac does not provide a way to fine-tune the treating of
    // warnings as errors. Either all are considered errors
    // (with -Xfatal-warnings), or none are. This hack analyzes the compiler's
    // output and treats all warnings as errors, except for deprecations.
    compile in Compile := {
      val log      = streams.value.log
      val compiled = (compile in Compile).value
      val problems = compiled.readSourceInfos().getAllSourceInfos.asScala.flatMap {
        case (_, info) => info.getReportedProblems
      }
      var deprecationsOnly = true
      problems.foreach { problem =>
        if (!problem.message().contains("is deprecated")) {
          deprecationsOnly = false
          log.error(s"[fatal warning] ${problem.message()}")
        }
      }
      if (!deprecationsOnly)
        throw new FatalWarningsException("Fatal warnings: some warnings other than deprecations were found.")
      compiled
    }
  )

  lazy val lintSettings = formatSettings ++ scalastyleSettings ++ scalacSettings

  override def projectSettings: Seq[Def.Setting[_]] = inConfig(Compile)(lintSettings) ++ inConfig(Test)(lintSettings)

}
case class FatalWarningsException(message: String) extends Exception(message)