Merge "Don't warn for Kotlin synthetic metadata found." into androidx-main am: aea998fd46

Original change: https://android-review.googlesource.com/c/platform/frameworks/support/+/2609040

Change-Id: I00448a7a010aa050661073452021f7f68dcb3204
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
index 83ccfd4..405c5a1 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
@@ -31,6 +31,7 @@
 import com.google.testing.compile.Compilation
 import java.util.regex.Pattern
 import javax.tools.Diagnostic
+import org.junit.AssumptionViolatedException
 
 /**
  * Holds the information about a test compilation result.
@@ -415,6 +416,11 @@
     internal fun assertNoProcessorAssertionErrors() {
         val processingException = compilationResult.processor.getProcessingException()
         if (processingException != null) {
+            // processor has an assumption violation, re-throw so test case does not generate
+            // a failure
+            if (processingException is AssumptionViolatedException) {
+                throw processingException
+            }
             // processor has an error which we want to throw but we also want the subject, hence
             // we wrap it
             throw CompilationAssertionError(
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
index 9e8131c..3a0e081 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
@@ -166,12 +166,23 @@
                     element
                 )
             }
-            // TODO: Support more metadata kind (file facade, synthetic class, etc...)
             return when (classMetadata) {
                 is KotlinClassMetadata.Class -> KmClassContainer(classMetadata.toKmClass())
+                // Synthetic classes generated for various Kotlin features ($DefaultImpls,
+                // $WhenMappings, etc) are ignored because the data contained does not affect
+                // the metadata derived APIs. These classes are never referenced by user code but
+                // could be discovered by processors when inspecting inner classes.
+                is KotlinClassMetadata.SyntheticClass,
+                // Multi file classes are also ignored, the elements contained in these might be
+                // referenced by user code in method bodies but not part of the AST, however it
+                // is possible for a processor to discover them by inspecting elements under a
+                // package.
+                is KotlinClassMetadata.FileFacade,
+                is KotlinClassMetadata.MultiFileClassFacade,
+                is KotlinClassMetadata.MultiFileClassPart -> null
                 else -> {
                     env.delegate.messager.printMessage(
-                        Diagnostic.Kind.WARNING,
+                        Diagnostic.Kind.ERROR,
                         "Unable to read Kotlin metadata due to unsupported metadata " +
                             "kind: $classMetadata.",
                         element
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
index 5915327..43c856b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
@@ -20,6 +20,7 @@
 import androidx.room.compiler.processing.XProcessingEnvConfig
 import androidx.room.compiler.processing.javac.JavacProcessingEnv
 import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runJavaProcessorTest
 import androidx.room.compiler.processing.util.runKaptTest
@@ -792,6 +793,111 @@
         }
     }
 
+    @Test
+    fun ignore_syntheticMetadata_defaultImpls() {
+        val src = Source.kotlin(
+            "Subject.kt",
+            """
+            interface Subject {
+              fun instance(): String = "Hello"
+            }
+            """.trimIndent()
+        )
+        simpleRun(
+            sources = listOf(src),
+            kotlincArgs = listOf("-Xjvm-default=disable")
+        ) {
+            val subjectElement = processingEnv.requireTypeElement("Subject.DefaultImpls")
+            // Call metadata derived API causing it to be read
+            assertThat(subjectElement.isKotlinObject()).isFalse()
+            assertCompilationResult {
+                hasNoWarnings()
+            }
+        }
+    }
+
+    @Test
+    fun ignore_syntheticMetadata_whenMappings() {
+        val src = Source.kotlin(
+            "Subject.kt",
+            """
+            class Subject {
+              enum class Fruit {
+                APPLE,
+                STRAWBERRY
+              }
+
+              fun printName(fruit: Fruit) {
+                println(
+                  when(fruit) {
+                    Fruit.APPLE -> "manzana"
+                    Fruit.STRAWBERRY -> "fresa"
+                  }
+                )
+              }
+            }
+            """.trimIndent()
+        )
+        simpleRun(
+            sources = listOf(src),
+        ) {
+            assertThat(processingEnv.findTypeElement("Subject.Fruit")).isNotNull()
+            val subjectElement = processingEnv.findTypeElement("Subject.WhenMappings")
+                // Currently $WhenMapping has the ACC_SYNTHETIC flag making it unreadable by
+                // annotation processors making it impossible to verify synthetic metadata is
+                // ignored.
+                ?: throw AssumptionViolatedException("No test if WhenMappings is not found")
+            // Call metadata derived API causing it to be read
+            assertThat(subjectElement.isKotlinObject()).isFalse()
+            assertCompilationResult {
+                hasNoWarnings()
+            }
+        }
+    }
+
+    @Test
+    fun ignore_fileFacadeMetadata() {
+        val aSrc = Source.kotlin(
+            "A.kt",
+            """
+            @file:JvmMultifileClass
+            @file:JvmName("Subject")
+
+            fun a() { }
+            """.trimIndent()
+        )
+        val bSrc = Source.kotlin(
+            "B.kt",
+            """
+            @file:JvmMultifileClass
+            @file:JvmName("Subject")
+
+            fun b() { }
+            """.trimIndent()
+        )
+        simpleRun(
+            sources = listOf(aSrc, bSrc),
+        ) {
+            // Find the multi file class facade element
+            val facadeElement = processingEnv.requireTypeElement("Subject")
+            // Call metadata derived API causing it to be read
+            assertThat(facadeElement.isKotlinObject()).isFalse()
+
+            // Try to find the multi file class part elements, currently these classes have the
+            // ACC_SYNTHETIC flag making them unreadable by annotation processors and impossible to
+            // verify that multi file metadata is ignored.
+            val facadePartOne = processingEnv.findTypeElement("Subject__AKt")
+                ?: throw AssumptionViolatedException("No test if MultiFileClassPart is not found")
+            assertThat(facadePartOne.isKotlinObject()).isFalse()
+            val facadePartTwo = processingEnv.findTypeElement("Subject__BKt")
+                ?: throw AssumptionViolatedException("No test if MultiFileClassPart is not found")
+            assertThat(facadePartTwo.isKotlinObject()).isFalse()
+            assertCompilationResult {
+                hasNoWarnings()
+            }
+        }
+    }
+
     private fun TypeElement.getDeclaredMethods() = ElementFilter.methodsIn(enclosedElements)
 
     private fun TypeElement.getDeclaredMethod(name: String) = getDeclaredMethods().first {
@@ -803,19 +909,24 @@
     @Suppress("NAME_SHADOWING") // intentional
     private fun simpleRun(
         sources: List<Source> = emptyList(),
-        handler: (ProcessingEnvironment) -> Unit
+        kotlincArgs: List<String> = emptyList(),
+        handler: XTestInvocation.(ProcessingEnvironment) -> Unit
     ) {
         val (sources, classpath) = if (preCompiled) {
             emptyList<Source>() to compileFiles(sources)
         } else {
             sources to emptyList()
         }
-        runKaptTest(sources = sources, classpath = classpath) {
+        runKaptTest(
+            sources = sources,
+            classpath = classpath,
+            kotlincArguments = kotlincArgs
+        ) {
             val processingEnv = it.processingEnv
             if (processingEnv !is JavacProcessingEnv) {
                 throw AssumptionViolatedException("This test only works for java/kapt compilation")
             }
-            handler(processingEnv.delegate)
+            it.handler(processingEnv.delegate)
         }
     }