KMT-1178: Added an inspection for functions with multiple @PreviewParameter annotations.

GitOrigin-RevId: bd840f3411a3cefcb5bc4fd5a08b411b6289bfac
This commit is contained in:
Ilia Bogdanovich
2025-07-01 12:45:19 +00:00
committed by intellij-monorepo-bot
parent 30f50c1309
commit afbec756c6
7 changed files with 167 additions and 0 deletions
@@ -0,0 +1,11 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.compose.ide.plugin.k1.inspections
import com.intellij.compose.ide.plugin.shared.inspections.MultiplatformPreviewMultipleParameterProvidersInspectionTest
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode
class K1MultiplatformPreviewMultipleParameterProvidersInspectionTest(previewPackageName: String, parametersPackageName: String)
: MultiplatformPreviewMultipleParameterProvidersInspectionTest(previewPackageName, parametersPackageName) {
override val pluginMode: KotlinPluginMode
get() = KotlinPluginMode.K1
}
@@ -0,0 +1,11 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.compose.ide.plugin.k2.inspections
import com.intellij.compose.ide.plugin.shared.inspections.MultiplatformPreviewMultipleParameterProvidersInspectionTest
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode
class K2MultiplatformPreviewMultipleParameterProvidersInspectionTest(previewPackageName: String, parametersPackageName: String)
: MultiplatformPreviewMultipleParameterProvidersInspectionTest(previewPackageName, parametersPackageName) {
override val pluginMode: KotlinPluginMode
get() = KotlinPluginMode.K2
}
@@ -0,0 +1,5 @@
<html>
<body>
Detects multiple @PreviewParameter annotations. At most one @PreviewParameter is allowed.
</body>
</html>
@@ -9,6 +9,7 @@
<overrideImplementsAnnotationsFilter id="ComposeOverrideImplementsAnnotationsFilter"
implementation="com.intellij.compose.ide.plugin.shared.ComposeOverrideImplementsAnnotationsFilter" />
<!--suppress PluginXmlCapitalization -->
<localInspection implementationClass="com.intellij.compose.ide.plugin.shared.inspections.MultiplatformPreviewAnnotationInFunctionWithParametersInspection"
language="kotlin"
enabledByDefault="true"
@@ -17,5 +18,14 @@
key="compose.preview.inspection.no.parameters.name"
level="ERROR"/>
<!--suppress PluginXmlCapitalization -->
<localInspection implementationClass="com.intellij.compose.ide.plugin.shared.inspections.MultiplatformPreviewMultipleParameterProvidersInspection"
language="kotlin"
enabledByDefault="true"
bundle="messages.ComposeIdeBundle"
groupKey="compose.preview.inspection.group.name"
key="compose.preview.inspection.no.multiple.preview.provider.name"
level="ERROR"/>
</extensions>
</idea-plugin>
@@ -10,3 +10,5 @@ compose.preview.inspection.group.name=Compose Multiplatform Preview
compose.preview.inspection.no.parameters.name=Preview used on a Composable function with parameters
compose.preview.inspection.no.parameters.description=Composable functions with non-default parameters are not supported in Preview unless \
they are annotated with @PreviewParameter
compose.preview.inspection.no.multiple.preview.provider.name=Multiple @PreviewParameter are not allowed
compose.preview.inspection.no.multiple.preview.provider.description=Multiple @PreviewParameter are not allowed
@@ -0,0 +1,51 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.compose.ide.plugin.shared.inspections
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.compose.ide.plugin.shared.ComposeIdeBundle
import com.intellij.compose.ide.plugin.shared.isPreviewParameterAnnotation
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtNamedFunction
/**
* Inspection that checks that any function annotated with `@Preview`, or with a MultiPreview, has
* at most one `@PreviewParameter`.
* Based on `com.android.tools.idea.compose.preview.PreviewMultipleParameterProvidersInspection`
*/
open class MultiplatformPreviewMultipleParameterProvidersInspection :
BasePreviewAnnotationInspection(
ComposeIdeBundle.message("compose.preview.inspection.group.name"),
ComposePreviewAnnotationChecker
) {
override fun visitPreviewAnnotation(
holder: ProblemsHolder,
function: KtNamedFunction,
previewAnnotation: KtAnnotationEntry,
) {
// Find the second PreviewParameter annotation if any
val secondPreviewParameter =
function.valueParameters
.mapNotNull { it.annotationEntries.firstOrNull { it.isPreviewParameterAnnotation() } }
.drop(1)
.firstOrNull() ?: return
// Flag the second annotation as the error
holder.registerProblem(
secondPreviewParameter as PsiElement,
ComposeIdeBundle.message("compose.preview.inspection.no.multiple.preview.provider.description"),
ProblemHighlightType.ERROR,
)
}
override fun visitPreviewAnnotation(
holder: ProblemsHolder,
annotationClass: KtClass,
previewAnnotation: KtAnnotationEntry,
) {
// This inspection only applies for functions, not for Annotation classes
return
}
}
@@ -0,0 +1,77 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.compose.ide.plugin.shared.inspections
import com.intellij.compose.ide.plugin.shared.COMPOSABLE_ANNOTATION_FQ_NAME
import com.intellij.compose.ide.plugin.shared.JETPACK_PREVIEW_TOOLING_PACKAGE
import com.intellij.compose.ide.plugin.shared.MULTIPLATFORM_PREVIEW_TOOLING_PACKAGE
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
/**
* Test for [MultiplatformPreviewMultipleParameterProvidersInspection].
*/
@RunWith(Parameterized::class)
abstract class MultiplatformPreviewMultipleParameterProvidersInspectionTest(
private val previewPackageName: String,
private val parametersPackageName: String,
) : PreviewInspectionTest() {
@Suppress("TestFunctionName")
@Test
fun test() = runPreviewInspectionTest(
MultiplatformPreviewMultipleParameterProvidersInspection(),
"""
import $previewPackageName.Preview
import $parametersPackageName.PreviewParameter
import $parametersPackageName.PreviewParameterProvider
import ${COMPOSABLE_ANNOTATION_FQ_NAME.asString()}
class IntProvider: PreviewParameterProvider<Int> {
override val values: Sequence<Int> = sequenceOf(1, 2)
}
@Preview
@Composable
fun PreviewWithMultipleProviders(@PreviewParameter(IntProvider::class) a: Int,
@PreviewParameter(IntProvider::class) b: Int) { // ERROR, only one PreviewParameter is supported
}
@Preview
annotation class MyAnnotation
@MyAnnotation
@Composable
fun MultiPreviewWithMultipleProviders(@PreviewParameter(IntProvider::class) a: Int,
@PreviewParameter(IntProvider::class) b: Int) { // ERROR, only one PreviewParameter is supported
}
@MyAnnotation
@Composable
fun MixedPreviewProviders1(@androidx.compose.ui.tooling.preview.PreviewParameter(IntProvider::class) a: Int,
@org.jetbrains.compose.ui.tooling.preview.PreviewParameter(IntProvider::class) b: Int) { // ERROR, only one PreviewParameter is supported
}
@MyAnnotation
@Composable
fun MixedPreviewProviders2(@org.jetbrains.compose.ui.tooling.preview.PreviewParameter(IntProvider::class) a: Int,
@androidx.compose.ui.tooling.preview.PreviewParameter(IntProvider::class) b: Int) { // ERROR, only one PreviewParameter is supported
}
""".trimIndent(),
"""|12: Multiple @PreviewParameter are not allowed
|21: Multiple @PreviewParameter are not allowed
|27: Multiple @PreviewParameter are not allowed
|33: Multiple @PreviewParameter are not allowed
""".trimMargin()
)
companion object {
@Parameterized.Parameters(name = "{0} & {1}")
@JvmStatic
fun data(): Array<Array<*>> = arrayOf(
arrayOf(JETPACK_PREVIEW_TOOLING_PACKAGE, JETPACK_PREVIEW_TOOLING_PACKAGE),
arrayOf(JETPACK_PREVIEW_TOOLING_PACKAGE, MULTIPLATFORM_PREVIEW_TOOLING_PACKAGE),
arrayOf(MULTIPLATFORM_PREVIEW_TOOLING_PACKAGE, JETPACK_PREVIEW_TOOLING_PACKAGE),
arrayOf(MULTIPLATFORM_PREVIEW_TOOLING_PACKAGE, MULTIPLATFORM_PREVIEW_TOOLING_PACKAGE),
)
}
}