Updates UnnecessaryLambdaCreationDetector to handle lambda subtypes

E.g:
interface Foo
class FooImpl : Foo

val onFooImpl: (Foo) -> Unit = {}
fun foo(onFoo: (FooImpl) -> Unit) {}

foo(onFooImpl) is valid

Also fixes a bug with the lint check not handling lambdas returned by a function, such as:

{ foo()() }

where fun foo(): () -> Unit

Test: UnnecessaryLambdaCreationDetectorTest
Change-Id: Ibea2208e35a6dfe4075e99b7f81c6f8a7dfa0ed2
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyAdaptingPlatformTextInputModifierNode.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyAdaptingPlatformTextInputModifierNode.kt
index 000655e..57c3a12 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyAdaptingPlatformTextInputModifierNode.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/LegacyAdaptingPlatformTextInputModifierNode.kt
@@ -108,9 +108,7 @@
     ): Job? {
         if (!isAttached) return null
         return coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) {
-            establishTextInputSession {
-                block()
-            }
+            establishTextInputSession(block)
         }
     }
 }
diff --git a/compose/lint/common/src/main/java/androidx/compose/lint/ComposableUtils.kt b/compose/lint/common/src/main/java/androidx/compose/lint/ComposableUtils.kt
index cc8ef54..0cdf01c 100644
--- a/compose/lint/common/src/main/java/androidx/compose/lint/ComposableUtils.kt
+++ b/compose/lint/common/src/main/java/androidx/compose/lint/ComposableUtils.kt
@@ -263,7 +263,7 @@
 /**
  * Returns whether this type reference is @Composable or not
  */
-private val UTypeReferenceExpression.isComposable: Boolean
+val UTypeReferenceExpression.isComposable: Boolean
     get() {
         if (type.hasAnnotation(Names.Runtime.Composable.javaFqn)) return true
 
diff --git a/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/UnnecessaryLambdaCreationDetector.kt b/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/UnnecessaryLambdaCreationDetector.kt
index 70c0df9..41c356b 100644
--- a/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/UnnecessaryLambdaCreationDetector.kt
+++ b/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/UnnecessaryLambdaCreationDetector.kt
@@ -39,8 +39,8 @@
 import org.jetbrains.uast.UCallExpression
 import org.jetbrains.uast.UElement
 import org.jetbrains.uast.ULambdaExpression
+import org.jetbrains.uast.UMethod
 import org.jetbrains.uast.UReturnExpression
-import org.jetbrains.uast.UUnknownExpression
 import org.jetbrains.uast.UVariable
 import org.jetbrains.uast.skipParenthesizedExprDown
 import org.jetbrains.uast.toUElement
@@ -73,18 +73,13 @@
      * This handler visits every lambda expression and reports an issue if the following criteria
      * (in order) hold true:
      *
-     * 1. There is only one expression inside the lambda.
-     * 2. The expression is a function call
-     * 3. The lambda is being invoked as part of a function call, and not as a property assignment
+     * 1. There is only one expression inside the lambda
+     * 2. The lambda literal is created as part of a function call, and not as a property assignment
      *    such as val foo = @Composable {}
-     * 4. The receiver type of the function call is `Function0` (i.e, we are invoking something
-     *    that matches `() -> Unit` - this both avoids non-lambda invocations but also makes sure
-     *    that we don't warn for lambdas that have parameters, such as @Composable (Int) -> Unit
-     *    - this cannot be inlined.)
-     * 5. The outer function call that contains this lambda is not a call to a `LayoutNode`
-     *    (because these are technically constructor invocations that we just intercept calls to
-     *    there is no way to avoid using a trailing lambda for this)
-     * 6. The lambda is not being passed as a parameter, for example `Foo { lambda -> lambda() }`
+     * 3. The expression is an invoke() call
+     * 4. The receiver type of the invoke call is a functional type, and it is a subtype of (i.e
+     * compatible to cast to) the lambda parameter functional type
+     * 5. The lambda parameter and literal have matching composability
      */
     class UnnecessaryLambdaCreationHandler(private val context: JavaContext) : UElementHandler() {
 
@@ -97,7 +92,7 @@
                 is UCallExpression -> expr
                 is UReturnExpression -> {
                     if (expr.sourcePsi == null) { // implicit return
-                        expr.returnExpression as? UCallExpression
+                        expr.returnExpression?.skipParenthesizedExprDown() as? UCallExpression
                     } else null
                 }
                 else -> null
@@ -126,9 +121,7 @@
             analyze(expressionSourcePsi) {
                 val functionType = dispatchReceiverType(expressionSourcePsi) ?: return
                 val argumentType = toLambdaFunctionalType(node) ?: return
-                  // TODO enable subType checking
-//                if (!(functionType isSubTypeOf argumentType)) return
-                if (!(functionType isEqualTo argumentType)) return
+                if (!(functionType isSubTypeOf argumentType)) return
             }
 
             val expectedComposable = node.isComposable
@@ -144,10 +137,17 @@
 
             val isComposable = when (resolvedLambdaSource) {
                 is UVariable -> resolvedLambdaSource.isComposable
-                // TODO: if the resolved source is a parameter in a local function, it
-                //  incorrectly returns an UnknownKotlinExpression instead of a UParameter
-                //  https://youtrack.jetbrains.com/issue/KTIJ-19125
-                is UUnknownExpression -> return
+                // If the source is a method, then the lambda is the return type of the method, so
+                // check the return type
+                is UMethod -> resolvedLambdaSource.returnTypeReference?.isComposable == true
+                // Safe return if we failed to resolve. This can happen for implicit `it` parameters
+                // that are lambdas, but this should only happen exceptionally for lambdas with
+                // an `Any` parameter, such as { any: Any -> }.let { it(Any()) }, since this passes
+                // the isSubTypeOf check above. In this case it isn't possible to inline this call,
+                // so no need to handle these implicit parameters.
+                null -> return
+                // Throw since this is an internal check, and we want to fix this for unknown types.
+                // If making this check public, it's safer to return instead without throwing.
                 else -> error(parentExpression.asSourceString())
             }
 
diff --git a/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/UnnecessaryLambdaCreationDetectorTest.kt b/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/UnnecessaryLambdaCreationDetectorTest.kt
index c512441..1c026c7 100644
--- a/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/UnnecessaryLambdaCreationDetectorTest.kt
+++ b/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/UnnecessaryLambdaCreationDetectorTest.kt
@@ -363,5 +363,59 @@
         """
         ).expectClean()
     }
+
+    @Test
+    fun warnsForFunctionsReturningALambda() {
+        check(
+            """
+            package test
+
+            import androidx.compose.runtime.Composable
+
+            fun returnsLambda(): () -> Unit = {}
+            fun returnsComposableLambda(): @Composable () -> Unit = {}
+
+            @Composable
+            fun Test() {
+                ComposableFunction {
+                    returnsLambda()()
+                }
+
+                InlineComposableFunction {
+                    returnsLambda()()
+                }
+
+                ReifiedComposableFunction<Any> {
+                    returnsLambda()()
+                }
+
+                ComposableFunction {
+                    returnsComposableLambda()()
+                }
+
+                InlineComposableFunction {
+                    returnsComposableLambda()()
+                }
+
+                ReifiedComposableFunction<Any> {
+                    returnsComposableLambda()()
+                }
+            }
+        """
+        ).expect(
+            """
+src/test/test.kt:23: Error: Creating an unnecessary lambda to emit a captured lambda [UnnecessaryLambdaCreation]
+        returnsComposableLambda()()
+                                 ~
+src/test/test.kt:27: Error: Creating an unnecessary lambda to emit a captured lambda [UnnecessaryLambdaCreation]
+        returnsComposableLambda()()
+                                 ~
+src/test/test.kt:31: Error: Creating an unnecessary lambda to emit a captured lambda [UnnecessaryLambdaCreation]
+        returnsComposableLambda()()
+                                 ~
+3 errors, 0 warnings
+        """
+        )
+    }
 }
 /* ktlint-enable max-line-length */
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/lazy/LazyList.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/lazy/LazyList.kt
index 2430387..f2faf26 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/lazy/LazyList.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/lazy/LazyList.kt
@@ -126,7 +126,7 @@
                 ?: (ReservedItemIdRangeEnd - index)
             check(id != LazyListScope.UnspecifiedItemId) { "Implicit list item ids exhausted." }
             LazyListItem(id, alignment) {
-                object : LazyItemScope { }.apply { composable() }
+                object : LazyItemScope { }.composable()
             }
         }
     }
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/lazy/LazyVerticalGrid.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/lazy/LazyVerticalGrid.kt
index 53a41ce..71d0724 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/lazy/LazyVerticalGrid.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/lazy/LazyVerticalGrid.kt
@@ -135,7 +135,7 @@
                 "Implicit list item ids exhausted."
             }
             LazyVerticalGridItem(id, alignment) {
-                object : LazyItemScope { }.apply { composable() }
+                object : LazyItemScope { }.composable()
             }
         }
     }
diff --git a/glance/glance-wear-tiles/src/main/java/androidx/glance/wear/tiles/curved/CurvedRow.kt b/glance/glance-wear-tiles/src/main/java/androidx/glance/wear/tiles/curved/CurvedRow.kt
index 58489b1..33afc1f 100644
--- a/glance/glance-wear-tiles/src/main/java/androidx/glance/wear/tiles/curved/CurvedRow.kt
+++ b/glance/glance-wear-tiles/src/main/java/androidx/glance/wear/tiles/curved/CurvedRow.kt
@@ -124,7 +124,7 @@
 
     return {
         curvedChildList.forEach { composable ->
-            object : CurvedChildScope {}.apply { composable() }
+            object : CurvedChildScope {}.composable()
         }
     }
 }