From ccc33eab5c080a681502f42b3c2e885c8865e4dc Mon Sep 17 00:00:00 2001 From: Tom Grigg Date: Tue, 26 Apr 2022 10:06:52 -0700 Subject: [PATCH] Survive missing Java inner annotation classfiles Co-authored-by: Seth Tisue --- .../dotc/core/classfile/ClassfileParser.scala | 11 ++++++++- .../test/dotty/tools/AnnotationsTests.scala | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 8550ffb5fd8e..a45ff756173a 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -1127,7 +1127,16 @@ class ClassfileParser( val outerName = entry.strippedOuter val innerName = entry.originalName val owner = classNameToSymbol(outerName) - val result = atPhase(typerPhase)(getMember(owner, innerName.toTypeName)) + val result = owner.denot.infoOrCompleter match + case _: StubInfo if hasAnnotation(entry.jflags) => + requiredClass(innerName.toTypeName) + // It's okay for the classfiles of Java annotations to be missing + // from the classpath. If an annotation is defined as an inner class + // we need to avoid forcing the outer class symbol here, and instead + // return a new stub symbol for the inner class. This is tested by + // `surviveMissingInnerClassAnnot` in AnnotationsTests.scala + case _ => + atPhase(typerPhase)(getMember(owner, innerName.toTypeName)) assert(result ne NoSymbol, i"""failure to resolve inner class: |externalName = ${entry.externalName}, diff --git a/compiler/test/dotty/tools/AnnotationsTests.scala b/compiler/test/dotty/tools/AnnotationsTests.scala index e7fc1242517c..59e9f3129294 100644 --- a/compiler/test/dotty/tools/AnnotationsTests.scala +++ b/compiler/test/dotty/tools/AnnotationsTests.scala @@ -66,3 +66,26 @@ class AnnotationsTest: s"A missing annotation while parsing a Java class should be silently ignored but: ${ctx.reporter.summary}") } } + + @Test def surviveMissingInnerClassAnnot: Unit = + withJavaCompiled( + VirtualJavaSource("Outer.java", + """|package a.b; + |public @interface Outer { public @interface Value { @interface Immutable {} } } + |""".stripMargin), + VirtualJavaSource("Baz.java", + """|package a.b; + |@Outer.Value.Immutable abstract class Baz {}""".stripMargin) + ) { javaOutputDir => + Files.delete(javaOutputDir.resolve("a/b/Outer.class")) + Files.delete(javaOutputDir.resolve("a/b/Outer$Value.class")) + Files.delete(javaOutputDir.resolve("a/b/Outer$Value$Immutable.class")) + inCompilerContext(javaOutputDir.toString + File.pathSeparator + TestConfiguration.basicClasspath) { + val cls = requiredClass("a.b.Baz") + val annots = cls.annotations.map(_.tree) + assert(annots == Nil, + s"class Baz should have no visible annotations since Outer.Value.Immutable is not on the classpath, but found: $annots") + assert(!ctx.reporter.hasErrors && !ctx.reporter.hasWarnings, + s"A missing annotation while parsing a Java class should be silently ignored but: ${ctx.reporter.summary}") + } + }