From a08088bcbf05cbd8993ea8c11b6edd4a351d9af9 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Wed, 2 Oct 2024 13:00:44 -0400 Subject: [PATCH] Fix false positives for `ComposeContentEmitterReturningValues` (#420) Resolves #419 --- CHANGELOG.md | 4 ++ .../ContentEmitterReturningValuesDetector.kt | 41 +++++++++++-------- ...ntentEmitterReturningValuesDetectorTest.kt | 19 +++++++++ docs/rules.md | 4 +- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52bacf4..9b5ff19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ Changelog **Unreleased** -------------- +- **Fix**: Fix false positives reported by `ComposeContentEmitterReturningValues`. +- **Fix**: Fix `content-emitters` configuration in docs. +- **Fix**: Fix link to multipreview annotations in docs. + 1.4.0 ----- diff --git a/compose-lint-checks/src/main/java/slack/lint/compose/ContentEmitterReturningValuesDetector.kt b/compose-lint-checks/src/main/java/slack/lint/compose/ContentEmitterReturningValuesDetector.kt index 1346794..6cf5acc 100644 --- a/compose-lint-checks/src/main/java/slack/lint/compose/ContentEmitterReturningValuesDetector.kt +++ b/compose-lint-checks/src/main/java/slack/lint/compose/ContentEmitterReturningValuesDetector.kt @@ -102,7 +102,7 @@ constructor( .findChildrenByClass() .filter { val method = it.toUElementOfType() ?: return@filter false - method.isComposable && !method.returnsUnitOrVoid(context.evaluator) + method.isComposable } // We don't want to analyze composables that are extension functions, as they might be // things like @@ -120,21 +120,24 @@ constructor( // We can start showing errors, for composables that emit at all (from the list of // known composables) - val directEmissionsReported = composableToEmissionCount.keys + val directEmissionsReported = composableToEmissionCount.filterValues { it > 0 }.keys for (composable in directEmissionsReported) { - context.report( - ISSUE, - composable, - context.getNameLocation(composable), - ISSUE.getExplanation(TextFormat.TEXT), - ) + if ( + composable.toUElementOfType()?.returnsUnitOrVoid(context.evaluator) != true + ) { + context.report( + ISSUE, + composable, + context.getNameLocation(composable), + ISSUE.getExplanation(TextFormat.TEXT), + ) + } } // Now we can give some extra passes through the list of composables, and try to get a more // accurate count. // We want to make sure that if these composables are using other composables in this file - // that emit UI, - // those are taken into account too. For example: + // that emit UI, those are taken into account too. For example: // @Composable fun Comp1() { Text("Hi") } // @Composable fun Comp2() { Text("Hola") } // @Composable fun Comp3() { Comp1() Comp2() } // This wouldn't be picked up at first, but @@ -156,16 +159,20 @@ constructor( // Here we have the settled data after all the needed passes, so we want to show errors for // them, if they were not caught already by the 1st emission loop currentMapping - .filterValues { it > 1 } + .filterValues { it > 0 } .filterNot { directEmissionsReported.contains(it.key) } .keys .forEach { composable -> - context.report( - ISSUE, - composable, - context.getNameLocation(composable), - ISSUE.getExplanation(TextFormat.TEXT), - ) + if ( + composable.toUElementOfType()?.returnsUnitOrVoid(context.evaluator) != true + ) { + context.report( + ISSUE, + composable, + context.getNameLocation(composable), + ISSUE.getExplanation(TextFormat.TEXT), + ) + } } } } diff --git a/compose-lint-checks/src/test/java/slack/lint/compose/ContentEmitterReturningValuesDetectorTest.kt b/compose-lint-checks/src/test/java/slack/lint/compose/ContentEmitterReturningValuesDetectorTest.kt index e8f52b7..da7e5d6 100644 --- a/compose-lint-checks/src/test/java/slack/lint/compose/ContentEmitterReturningValuesDetectorTest.kt +++ b/compose-lint-checks/src/test/java/slack/lint/compose/ContentEmitterReturningValuesDetectorTest.kt @@ -176,4 +176,23 @@ class ContentEmitterReturningValuesDetectorTest : BaseComposeLintTest() { .trimIndent() lint().files(*commonStubs, kotlin(code)).run().expectClean() } + + // /~https://github.com/slackhq/compose-lints/issues/419 + @Test + fun `ensure happy path returning is valid`() { + @Language("kotlin") + val code = + """ + import androidx.compose.runtime.Composable + + @Composable + fun rememberInsetsController(): WindowInsetsControllerCompat? { + val view = LocalView.current + val window = remember { view.context.findActivity()?.window } ?: return null + return remember { WindowCompat.getInsetsController(window, view) } + } + """ + .trimIndent() + lint().files(*commonStubs, kotlin(code)).run().expectClean() + } } diff --git a/docs/rules.md b/docs/rules.md index cff0e5e..8454a45 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -90,7 +90,7 @@ Related rule: [`ComposeContentEmitterReturningValues`](/~https://github.com/slackh ```xml - ``` @@ -144,7 +144,7 @@ Related rule: [`ComposeMultipleContentEmitters`](/~https://github.com/slackhq/comp ```xml - ```