[go: nahoru, domu]

Merge "Annotate composables in frontend instead of backend" into androidx-main
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index eed900c..de0dc0e 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -139,7 +139,6 @@
               used(a)
               Example(class <no name provided> : A {
                 @Composable
-                @Composable
                 override fun compute(it: Int, %composer: Composer?, %changed: Int) {
                   %composer = %composer.startRestartGroup(<>)
                   sourceInformation(%composer, "C(compute)<comput...>:Test.kt")
@@ -214,7 +213,6 @@
               if (%changed !== 0 || !%composer.skipping) {
                 Button(class <no name provided> : ButtonColors {
                   @Composable
-                  @Composable
                   override fun getColor(%composer: Composer?, %changed: Int): Color {
                     %composer.startReplaceableGroup(<>)
                     sourceInformation(%composer, "C(getColor)<condit...>:Test.kt")
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt
new file mode 100644
index 0000000..42eba74
--- /dev/null
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin
+
+class SanityCheckCodegenTests : AbstractCodegenTest() {
+
+    fun testCallAbstractSuperWithTypeParameters() = ensureSetup {
+        testCompile(
+            """
+                abstract class AbstractB<Type>(d: Type) : AbstractA<Int, Type>(d) {
+                    override fun test(key: Int): Type {
+                        return super.test(key)
+                    }
+                }
+                abstract class AbstractA<Type1, Type2>(var d: Type2) {
+                    open fun test(key: Type1): Type2 = d
+                }
+        """
+        )
+    }
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
index 502a530..e1d3fb1 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
@@ -22,6 +22,7 @@
 import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
 import org.jetbrains.kotlin.descriptors.annotations.Annotations
 import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
+import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
 import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
 import org.jetbrains.kotlin.ir.types.IrType
 import org.jetbrains.kotlin.ir.util.hasAnnotation
@@ -75,6 +76,15 @@
     return replaceAnnotations(Annotations.create(annotations + annotation))
 }
 
+fun AnonymousFunctionDescriptor.annotateAsComposable(module: ModuleDescriptor) =
+    AnonymousFunctionDescriptor(
+        containingDeclaration,
+        Annotations.create(annotations + ComposeFqNames.makeComposableAnnotation(module)),
+        kind,
+        source,
+        isSuspend
+    )
+
 fun IrType.hasComposableAnnotation(): Boolean =
     hasAnnotation(ComposeFqNames.Composable)
 
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
index 8442ea1..b35e784 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
@@ -26,7 +26,6 @@
 import androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor
 import androidx.compose.compiler.plugins.kotlin.lower.KlibAssignableParamTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer
-import androidx.compose.compiler.plugins.kotlin.lower.annotateComposableFunctions
 import androidx.compose.compiler.plugins.kotlin.lower.decoys.CreateDecoysTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.decoys.RecordDecoySignaturesTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.decoys.SubstituteDecoyCallsTransformer
@@ -67,8 +66,6 @@
         // create a symbol remapper to be used across all transforms
         val symbolRemapper = ComposableSymbolRemapper()
 
-        moduleFragment.annotateComposableFunctions(pluginContext)
-
         ClassStabilityTransformer(
             pluginContext,
             symbolRemapper,
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeTypeResolutionInterceptorExtension.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeTypeResolutionInterceptorExtension.kt
index a7ae8a8..e9efd60 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeTypeResolutionInterceptorExtension.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeTypeResolutionInterceptorExtension.kt
@@ -50,6 +50,7 @@
             // If the expected type has an @Composable annotation then the literal function
             // expression should infer a an @Composable annotation
             context.trace.record(INFERRED_COMPOSABLE_DESCRIPTOR, descriptor, true)
+            return descriptor.annotateAsComposable(context.scope.ownerDescriptor.module)
         }
         val arg = getArgumentDescriptor(expression.functionLiteral, context.trace.bindingContext)
 
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrComposableAnnotator.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrComposableAnnotator.kt
deleted file mode 100644
index 8062136..0000000
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrComposableAnnotator.kt
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.compiler.plugins.kotlin.lower
-
-import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
-import androidx.compose.compiler.plugins.kotlin.hasComposableAnnotation
-import androidx.compose.compiler.plugins.kotlin.isMarkedAsComposable
-import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
-import org.jetbrains.kotlin.descriptors.FunctionDescriptor
-import org.jetbrains.kotlin.ir.IrElement
-import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
-import org.jetbrains.kotlin.ir.declarations.IrAttributeContainer
-import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
-import org.jetbrains.kotlin.ir.declarations.IrField
-import org.jetbrains.kotlin.ir.declarations.IrFunction
-import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
-import org.jetbrains.kotlin.ir.declarations.IrProperty
-import org.jetbrains.kotlin.ir.declarations.IrValueParameter
-import org.jetbrains.kotlin.ir.declarations.IrVariable
-import org.jetbrains.kotlin.ir.expressions.IrBody
-import org.jetbrains.kotlin.ir.expressions.IrCall
-import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
-import org.jetbrains.kotlin.ir.expressions.IrContainerExpression
-import org.jetbrains.kotlin.ir.expressions.IrExpression
-import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
-import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
-import org.jetbrains.kotlin.ir.expressions.IrReturn
-import org.jetbrains.kotlin.ir.expressions.IrVararg
-import org.jetbrains.kotlin.ir.expressions.IrWhen
-import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
-import org.jetbrains.kotlin.ir.types.toKotlinType
-import org.jetbrains.kotlin.ir.util.constructors
-import org.jetbrains.kotlin.ir.util.defaultType
-import org.jetbrains.kotlin.ir.util.substitute
-import org.jetbrains.kotlin.ir.util.typeSubstitutionMap
-import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
-import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
-
-fun IrModuleFragment.annotateComposableFunctions(pluginContext: IrPluginContext):
-    Collection<IrAttributeContainer> {
-        return IrComposableAnnotator(pluginContext)
-            .apply { this@annotateComposableFunctions.acceptChildrenVoid(this) }
-            .composables
-    }
-
-private class IrComposableAnnotator(val pluginContext: IrPluginContext) : IrElementVisitorVoid {
-
-    val composables = mutableSetOf<IrAttributeContainer>()
-    val expectedComposable = mutableMapOf<IrElement, Boolean>()
-    val expectedReturnComposable = mutableMapOf<FunctionDescriptor, Boolean>()
-
-    @ObsoleteDescriptorBasedAPI
-    override fun visitFunction(declaration: IrFunction) {
-
-        expectedReturnComposable.put(
-            declaration.descriptor,
-            declaration.returnType.toKotlinType().hasComposableAnnotation()
-        )
-
-        if (expectedComposable.get(declaration) == true) {
-            declaration.setComposableAnnotation()
-        }
-
-        if (declaration.hasComposableAnnotation())
-            composables.add((declaration as IrAttributeContainer).attributeOwnerId)
-
-        super.visitFunction(declaration)
-    }
-
-    private fun IrFunction.setComposableAnnotation() {
-        if (hasComposableAnnotation()) return
-        val composableAnnotation = pluginContext.referenceClass(ComposeFqNames.Composable)!!.owner
-        annotations = annotations + listOf(
-            IrConstructorCallImpl.fromSymbolOwner(
-                type = composableAnnotation.defaultType,
-                constructorSymbol = composableAnnotation.constructors.first().symbol
-            )
-        )
-    }
-
-    @OptIn(ObsoleteDescriptorBasedAPI::class)
-    override fun visitField(declaration: IrField) {
-        declaration.initializer?.let { initializer ->
-            expectedComposable.put(
-                initializer,
-                declaration.type.toKotlinType().hasComposableAnnotation()
-            )
-        }
-        super.visitField(declaration)
-    }
-
-    @OptIn(ObsoleteDescriptorBasedAPI::class)
-    override fun visitCall(expression: IrCall) {
-        val declaration = expression.symbol.owner
-        if (declaration.hasComposableAnnotation())
-            composables.add(declaration.attributeOwnerId)
-
-        val irFunction = expression.symbol.owner
-        irFunction.valueParameters.forEachIndexed { index, it ->
-            val arg = expression.getValueArgument(index)
-            if (arg != null) {
-                val parameter = it.type.substitute(expression.typeSubstitutionMap)
-                val isComposable = parameter.hasComposableAnnotation()
-                expectedComposable[arg] = isComposable
-            }
-        }
-        super.visitCall(expression)
-    }
-
-    @OptIn(ObsoleteDescriptorBasedAPI::class)
-    override fun visitConstructorCall(expression: IrConstructorCall) {
-        val irFunction = expression.symbol.owner
-        irFunction.valueParameters.forEachIndexed { index, it ->
-            val arg = expression.getValueArgument(index)
-            if (arg != null) {
-                val parameter = it.type.substitute(expression.typeSubstitutionMap)
-                val isComposable = parameter.hasComposableAnnotation()
-                expectedComposable[arg] = isComposable
-            }
-        }
-        super.visitConstructorCall(expression)
-    }
-
-    @OptIn(ObsoleteDescriptorBasedAPI::class)
-    override fun visitValueParameter(declaration: IrValueParameter) {
-        declaration.defaultValue?.let { defaultValue ->
-            expectedComposable.put(
-                defaultValue,
-                declaration.type.toKotlinType().hasComposableAnnotation()
-            )
-        }
-        super.visitValueParameter(declaration)
-    }
-
-    @OptIn(ObsoleteDescriptorBasedAPI::class)
-    override fun visitExpression(expression: IrExpression) {
-        val expectedType = expectedComposable.get(expression)
-        when (expression) {
-            is IrFunctionExpression ->
-                expectedComposable.put(
-                    expression.function,
-                    expression.type.hasComposableAnnotation()
-                )
-            is IrExpressionBody ->
-                if (expectedType != null)
-                    expectedComposable.put(expression.expression, expectedType)
-            is IrReturn -> {
-                val expectedReturnType = expectedReturnComposable.get(
-                    expression.returnTargetSymbol.descriptor
-                ) ?: false
-                expectedComposable.put(expression.value, expectedReturnType)
-            }
-            is IrVararg -> {
-                expression.elements.forEach {
-                    expectedComposable.put(it, expression.type.hasComposableAnnotation())
-                }
-            }
-        }
-        super.visitExpression(expression)
-    }
-
-    override fun visitWhen(expression: IrWhen) {
-        val expectedType = expectedComposable.get(expression)
-        if (expectedType != null)
-            expression.branches.forEach {
-                expectedComposable.put(it.result, expectedType)
-            }
-        super.visitWhen(expression)
-    }
-
-    override fun visitBody(body: IrBody) {
-        val expectedType = expectedComposable.get(body)
-        when (body) {
-            is IrExpressionBody ->
-                if (expectedType != null)
-                    expectedComposable.put(body.expression, expectedType)
-        }
-        super.visitBody(body)
-    }
-
-    @OptIn(ObsoleteDescriptorBasedAPI::class)
-    override fun visitDeclaration(declaration: IrDeclarationBase) {
-        when (declaration) {
-            is IrProperty -> {
-                declaration.getter?.let { getter ->
-                    expectedComposable.put(getter, getter.descriptor.isMarkedAsComposable())
-                }
-                declaration.setter?.let { setter ->
-                    expectedComposable.put(setter, setter.descriptor.isMarkedAsComposable())
-                }
-            }
-            is IrVariable -> {
-                declaration.initializer?.let { initializer ->
-                    expectedComposable.put(
-                        initializer,
-                        declaration.type.toKotlinType().hasComposableAnnotation()
-                    )
-                }
-            }
-        }
-        super.visitDeclaration(declaration)
-    }
-
-    override fun visitContainerExpression(expression: IrContainerExpression) {
-        val expectedType = expectedComposable.get(expression)
-        if (expectedType != null && expression.statements.size > 0)
-            expectedComposable.put(
-                expression.statements.last(),
-                expectedType
-            )
-        super.visitContainerExpression(expression)
-    }
-
-    override fun visitElement(element: IrElement) {
-        element.acceptChildrenVoid(this)
-    }
-}