Skip to content

Commit

Permalink
Recurse preview annotations to check for inherited Previews
Browse files Browse the repository at this point in the history
Resolves #377
  • Loading branch information
ZacSweers committed Oct 1, 2024
1 parent e2f7ef4 commit 7d66761
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package slack.lint.compose.util

import org.jetbrains.uast.UAnnotated
import org.jetbrains.uast.UParameter
import org.jetbrains.uast.toUElementOfType

private const val COMPOSE_PREVIEW = "androidx.compose.ui.tooling.preview.Preview"
private const val COMPOSE_DESKTOP_PREVIEW = "androidx.compose.desktop.ui.tooling.preview.Preview"
Expand All @@ -18,15 +19,22 @@ val TEST_ANNOTATIONS =
)

val UAnnotated.isPreview: Boolean
get() =
uAnnotations.any {
it.resolve()?.let { cls ->
cls.qualifiedName in PREVIEW_ANNOTATIONS ||
// Is the annotation itself a preview-annotated annotation?
cls.hasAnnotation(COMPOSE_PREVIEW) ||
cls.hasAnnotation(COMPOSE_DESKTOP_PREVIEW)
} ?: false
}
get() = checkIsPreview(0, maxDepth = 4)

/**
* Previews can go multiple layers so we can recurse up to check. In [UAnnotated.isPreview] we cap
* it at 4 to be reasonable.
*/
private fun UAnnotated.checkIsPreview(depth: Int, maxDepth: Int): Boolean {
if (depth >= maxDepth) return false
return uAnnotations.any {
it.resolve()?.let { cls ->
cls.qualifiedName in PREVIEW_ANNOTATIONS ||
// Is the annotation itself a preview-annotated annotation?
cls.toUElementOfType<UAnnotated>()?.checkIsPreview(depth + 1, maxDepth) == true
} ?: false
}
}

val UAnnotated.isVisibleForTesting: Boolean
get() =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,36 @@ class ModifierMissingDetectorTest : BaseComposeLintTest() {
lint().files(*commonStubs, kotlin(code)).run().expectClean()
}

// https://github.com/slackhq/compose-lints/issues/377
@Test
fun `deeply nested previews can still be detected and allowed`() {
@Language("kotlin")
val code =
"""
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.Modifier
@Preview
annotation class DeviceWidthPreviews
@DeviceWidthPreviews
annotation class ThemePreviews
@ThemePreviews
annotation class ComponentPreviews
@ComponentPreviews
@Composable
fun Something() {
Text("Hi")
}
"""
.trimIndent()

lint().files(*commonStubs, kotlin(code)).run().expectClean()
}

@Test
fun `non content emitting root composables are ignored`() {
@Language("kotlin")
Expand Down

0 comments on commit 7d66761

Please sign in to comment.