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)
- }
-}