From 66e5ad3482317098ac3bcfb76cabe43c06e600cc Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 6 Nov 2020 18:10:46 +0100 Subject: [PATCH] Fix #10198: Be robust against missing annotations In 6f32b6299833, I made sure the compiler could survive parsing a Java classfile referring to annotations missing from the classpath, but we could still run into trouble when a Scala unpickled class does the same. Tweaking the Tasty unpickler to drop missing annotations would require always forcing the symbols of all annotations which I would rather avoid doing. Instead, we adopt the same solution as Scala 2 did in https://github.com/sbt/zinc/pull/701 and special-case the single place where we force annotations of inherited members to ignore missing annotations. --- compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala | 10 ++++++---- .../source-dependencies/missing-annot/a/A.scala | 4 ++++ .../missing-annot/a/JavaAnnot.java | 3 +++ .../missing-annot/a/ScalaAnnot.scala | 1 + .../source-dependencies/missing-annot/b/B.scala | 1 + .../source-dependencies/missing-annot/build.sbt | 9 +++++++++ .../missing-annot/project/DottyInjectedPlugin.scala | 11 +++++++++++ .../missing-annot/project/plugins.sbt | 1 + .../sbt-test/source-dependencies/missing-annot/test | 8 ++++++++ 9 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 sbt-dotty/sbt-test/source-dependencies/missing-annot/a/A.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/missing-annot/a/JavaAnnot.java create mode 100644 sbt-dotty/sbt-test/source-dependencies/missing-annot/a/ScalaAnnot.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/missing-annot/b/B.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/missing-annot/build.sbt create mode 100644 sbt-dotty/sbt-test/source-dependencies/missing-annot/project/DottyInjectedPlugin.scala create mode 100644 sbt-dotty/sbt-test/source-dependencies/missing-annot/project/plugins.sbt create mode 100644 sbt-dotty/sbt-test/source-dependencies/missing-annot/test diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index c7a05fe2f284..863909066161 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -604,10 +604,12 @@ private class ExtractAPICollector(using Context) extends ThunkHolder { // In the Scala2 ExtractAPI phase we only extract annotations that extend // StaticAnnotation, but in Dotty we currently pickle all annotations so we - // extract everything (except inline body annotations which are handled - // above). - s.annotations.filter(_.symbol != defn.BodyAnnot) foreach { annot => - annots += apiAnnotation(annot) + // extract everything (except annotations missing from the classpath which + // we simply skip over, and inline body annotations which are handled above). + s.annotations.foreach { annot => + val sym = annot.symbol + if sym.exists && sym != defn.BodyAnnot then + annots += apiAnnotation(annot) } annots.toList diff --git a/sbt-dotty/sbt-test/source-dependencies/missing-annot/a/A.scala b/sbt-dotty/sbt-test/source-dependencies/missing-annot/a/A.scala new file mode 100644 index 000000000000..77ab340e6e4a --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/missing-annot/a/A.scala @@ -0,0 +1,4 @@ +class A { + @JavaAnnot def foo: Int = 1 + @ScalaAnnot def bar: Int = 1 +} diff --git a/sbt-dotty/sbt-test/source-dependencies/missing-annot/a/JavaAnnot.java b/sbt-dotty/sbt-test/source-dependencies/missing-annot/a/JavaAnnot.java new file mode 100644 index 000000000000..745105f178ea --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/missing-annot/a/JavaAnnot.java @@ -0,0 +1,3 @@ +import java.lang.annotation.*; + +public @interface JavaAnnot {} diff --git a/sbt-dotty/sbt-test/source-dependencies/missing-annot/a/ScalaAnnot.scala b/sbt-dotty/sbt-test/source-dependencies/missing-annot/a/ScalaAnnot.scala new file mode 100644 index 000000000000..8b0ee32e9dbd --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/missing-annot/a/ScalaAnnot.scala @@ -0,0 +1 @@ +class ScalaAnnot extends scala.annotation.StaticAnnotation diff --git a/sbt-dotty/sbt-test/source-dependencies/missing-annot/b/B.scala b/sbt-dotty/sbt-test/source-dependencies/missing-annot/b/B.scala new file mode 100644 index 000000000000..a18aec3dbe9b --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/missing-annot/b/B.scala @@ -0,0 +1 @@ +class B extends A diff --git a/sbt-dotty/sbt-test/source-dependencies/missing-annot/build.sbt b/sbt-dotty/sbt-test/source-dependencies/missing-annot/build.sbt new file mode 100644 index 000000000000..350b46c7b27b --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/missing-annot/build.sbt @@ -0,0 +1,9 @@ +lazy val a = project.in(file("a")) + .settings( + Compile / classDirectory := (ThisBuild / baseDirectory).value / "a-output" + ) + +lazy val b = project.in(file("b")) + .settings( + Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "b-input" + ) diff --git a/sbt-dotty/sbt-test/source-dependencies/missing-annot/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/missing-annot/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/missing-annot/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/missing-annot/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/missing-annot/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/missing-annot/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/missing-annot/test b/sbt-dotty/sbt-test/source-dependencies/missing-annot/test new file mode 100644 index 000000000000..1e8071204c47 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/missing-annot/test @@ -0,0 +1,8 @@ +> a/compile +$ mkdir b-input +# Add A to the classpath of b but not the annotations referenced by the members of A +$ copy-file a-output/A.class b-input/A.class +$ copy-file a-output/A.tasty b-input/A.tasty +# B should still be able to compile even though ExtractAPI forces the +# annotations of all inherited members. +> b/compile