Skip to content

Commit 66e5ad3

Browse files
committed
Fix #10198: Be robust against missing annotations
In 6f32b62, 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 sbt/zinc#701 and special-case the single place where we force annotations of inherited members to ignore missing annotations.
1 parent 7b5cdba commit 66e5ad3

File tree

9 files changed

+44
-4
lines changed

9 files changed

+44
-4
lines changed

compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -604,10 +604,12 @@ private class ExtractAPICollector(using Context) extends ThunkHolder {
604604

605605
// In the Scala2 ExtractAPI phase we only extract annotations that extend
606606
// StaticAnnotation, but in Dotty we currently pickle all annotations so we
607-
// extract everything (except inline body annotations which are handled
608-
// above).
609-
s.annotations.filter(_.symbol != defn.BodyAnnot) foreach { annot =>
610-
annots += apiAnnotation(annot)
607+
// extract everything (except annotations missing from the classpath which
608+
// we simply skip over, and inline body annotations which are handled above).
609+
s.annotations.foreach { annot =>
610+
val sym = annot.symbol
611+
if sym.exists && sym != defn.BodyAnnot then
612+
annots += apiAnnotation(annot)
611613
}
612614

613615
annots.toList
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class A {
2+
@JavaAnnot def foo: Int = 1
3+
@ScalaAnnot def bar: Int = 1
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import java.lang.annotation.*;
2+
3+
public @interface JavaAnnot {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class ScalaAnnot extends scala.annotation.StaticAnnotation
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class B extends A
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
lazy val a = project.in(file("a"))
2+
.settings(
3+
Compile / classDirectory := (ThisBuild / baseDirectory).value / "a-output"
4+
)
5+
6+
lazy val b = project.in(file("b"))
7+
.settings(
8+
Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "b-input"
9+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sbt._
2+
import Keys._
3+
4+
object DottyInjectedPlugin extends AutoPlugin {
5+
override def requires = plugins.JvmPlugin
6+
override def trigger = allRequirements
7+
8+
override val projectSettings = Seq(
9+
scalaVersion := sys.props("plugin.scalaVersion")
10+
)
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version"))
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
> a/compile
2+
$ mkdir b-input
3+
# Add A to the classpath of b but not the annotations referenced by the members of A
4+
$ copy-file a-output/A.class b-input/A.class
5+
$ copy-file a-output/A.tasty b-input/A.tasty
6+
# B should still be able to compile even though ExtractAPI forces the
7+
# annotations of all inherited members.
8+
> b/compile

0 commit comments

Comments
 (0)