diff --git a/build.mill b/build.mill index ca4043efb917..fa8d7050b9c3 100644 --- a/build.mill +++ b/build.mill @@ -191,9 +191,10 @@ object Deps { val sonatypeCentralClient = ivy"com.lumidion::sonatype-central-client-requests:0.3.0" object RuntimeDeps { - val sbtTestInterface = ivy"com.github.sbt:junit-interface:0.13.2" + val errorProneCore = ivy"com.google.errorprone:error_prone_core:2.31.0" val jupiterInterface = ivy"com.github.sbt.junit:jupiter-interface:0.11.4" - def all = Seq(sbtTestInterface, jupiterInterface) + val sbtTestInterface = ivy"com.github.sbt:junit-interface:0.13.2" + def all = Seq(errorProneCore, jupiterInterface, sbtTestInterface) } /** Used to manage transitive versions. */ diff --git a/contrib/errorprone/readme.adoc b/contrib/errorprone/readme.adoc new file mode 100644 index 000000000000..a0d4bc601e62 --- /dev/null +++ b/contrib/errorprone/readme.adoc @@ -0,0 +1,20 @@ += Mill ErrorProne Plugin + +== Caveats / JVM Options + +If you're on Java 17 or newer, you need to add the following options to your `.mill-jvm-opts` file in your project directory. + +.Required options in `.mill-jvm-opts` +---- +--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED +--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED +--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED +--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED +---- + diff --git a/contrib/errorprone/src/mill/contrib/errorprone/ErrorProneModule.scala b/contrib/errorprone/src/mill/contrib/errorprone/ErrorProneModule.scala new file mode 100644 index 000000000000..547d9a3ce17c --- /dev/null +++ b/contrib/errorprone/src/mill/contrib/errorprone/ErrorProneModule.scala @@ -0,0 +1,43 @@ +package mill.contrib.errorprone + +import mill.api.PathRef +import mill.{Agg, T} +import mill.scalalib.{Dep, DepSyntax, JavaModule} + +import java.io.File + +trait ErrorProneModule extends JavaModule { + def errorProneVersion: T[String] = T.input { + BuildInfo.errorProneVersion + } + def errorProneDeps: T[Agg[Dep]] = T { + Agg( + ivy"com.google.errorprone:error_prone_core:${errorProneVersion()}" + ) + } + def errorProneClasspath: T[Agg[PathRef]] = T { + resolveDeps(T.task { errorProneDeps().map(bindDependency()) })() + } + def errorProneJavacOptions: T[Seq[String]] = T { + val processorPath = errorProneClasspath().map(_.path).mkString(File.pathSeparator) + Seq( + "-XDcompilePolicy=simple", + "-processorpath", + processorPath, + "-Xplugin:ErrorProne" + ) +// val java17Options = Seq( +// "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", +// "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", +// "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", +// "--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED", +// "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", +// "--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", +// "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", +// "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", +// "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", +// "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED" +// ) + } + override def javacOptions: T[Seq[String]] = super.javacOptions() ++ errorProneJavacOptions() +} diff --git a/contrib/errorprone/test/resources/simple/src/ShortSet.java b/contrib/errorprone/test/resources/simple/src/ShortSet.java new file mode 100644 index 000000000000..5b1588d9620a --- /dev/null +++ b/contrib/errorprone/test/resources/simple/src/ShortSet.java @@ -0,0 +1,16 @@ +package simple.src; + +import java.util.HashSet; +import java.util.Set; + +public class ShortSet { + public static void main (String[] args) { + Set s = new HashSet<>(); + for (short i = 0; i < 100; i++) { + s.add(i); + s.remove(i - 1); + } + System.out.println(s.size()); + } +} + diff --git a/contrib/errorprone/test/src/mill/contrib/errorprone/ErrorProneTests.scala b/contrib/errorprone/test/src/mill/contrib/errorprone/ErrorProneTests.scala new file mode 100644 index 000000000000..87d92c44915a --- /dev/null +++ b/contrib/errorprone/test/src/mill/contrib/errorprone/ErrorProneTests.scala @@ -0,0 +1,35 @@ +package mill.contrib.errorprone + +import mill.scalalib.JavaModule +import mill.testkit.{TestBaseModule, UnitTester} +import os.Path +import utest._ + +object ErrorProneTests extends TestSuite { + + object noErrorProne extends TestBaseModule with JavaModule {} + object errorProne extends TestBaseModule with JavaModule with ErrorProneModule {} + + val testModuleSourcesPath: Path = os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) / "simple" + + def tests = Tests { + test("reference") { + test("compile") { + val eval = UnitTester(noErrorProne, testModuleSourcesPath) + val res = eval(noErrorProne.compile) + assert(res.isRight) + } + } + test("errorprone") { + test("compileFail") { + // we require additional JVM options, which we can't set at runtime, see contrib/errorprone/readme.adoc + if (scala.util.Properties.isJavaAtLeast(16)) "skipping test on Java 16+" + else { + val eval = UnitTester(errorProne, testModuleSourcesPath) + val res = eval(errorProne.compile) + assert(res.isLeft) + } + } + } + } +} diff --git a/contrib/package.mill b/contrib/package.mill index f091685d9cb6..3c5ee5bda6b5 100644 --- a/contrib/package.mill +++ b/contrib/package.mill @@ -17,6 +17,7 @@ import mill.resolve.SelectMode import mill.contrib.buildinfo.BuildInfo import mill.T import mill.define.Cross +import build.Deps // plugins and dependencies import $meta._ @@ -226,4 +227,12 @@ object `package` extends RootModule { def compileModuleDeps = Seq(build.scalalib) def testModuleDeps = super.testModuleDeps ++ Seq(build.scalalib) } + + object errorprone extends ContribModule with BuildInfo { + def compileModuleDeps = Seq(build.scalalib) + def testModuleDeps = super.testModuleDeps ++ Seq(build.scalalib) + def buildInfoPackageName = "mill.contrib.errorprone" + def buildInfoObjectName = "BuildInfo" + def buildInfoMembers = Seq(BuildInfo.Value("errorProneVersion", Deps.RuntimeDeps.errorProneCore.version)) + } }