[junit5 inspection] support a default value for FieldSource and MethodSource IDEA-374913

GitOrigin-RevId: 0c5e13e292528a091e53b3ca639a86dfc4f6a2c7
This commit is contained in:
Aleksey Dobrynin
2025-08-04 18:54:26 +00:00
committed by intellij-monorepo-bot
parent 5b55ec1015
commit f35b6836ef
12 changed files with 142 additions and 6 deletions
@@ -1,10 +1,11 @@
package com.intellij.jvm.analysis.testFramework
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection
/**
* A test base for testing [com.intellij.codeInsight.daemon.ImplicitUsageProvider] implementations in all JVM languages.
*/
abstract class JvmImplicitUsageProviderTestBase : JvmInspectionTestBase() {
override val inspection by lazy { UnusedDeclarationInspection(true) }
override val inspection: InspectionProfileEntry by lazy { UnusedDeclarationInspection(true) }
}
@@ -179,6 +179,45 @@ class JavaJunit5ImplicitUsageProviderTest : JUnit5ImplicitUsageProviderTestBase(
""".trimIndent())
}
fun `test usage of method source with method name`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import java.util.stream.*;
class MyTest {
@org.junit.jupiter.params.ParameterizedTest
@org.junit.jupiter.params.provider.MethodSource("bar")
void foo(String input) {
System.out.println(input);
}
private static Stream<String> <warning descr="Private method 'foo()' is never used">foo</warning>() {
return Stream.of("");
}
private static Stream<String> bar() {
return Stream.of("");
}
}
""".trimIndent())
}
fun `test usage of field source with field name`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import java.util.stream.*;
class MyTest {
@org.junit.jupiter.params.ParameterizedTest
@org.junit.jupiter.params.provider.FieldSource("bar")
void foo(String input) {
System.out.println(input);
}
private static Stream<String> <warning descr="Private field 'foo' is never used">foo</warning> = Stream.of("");
private static Stream<String> bar = Stream.of("");
}
""".trimIndent())
}
fun `test implicit usage of TempDir as direct annotation`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
class Test {
@@ -21,6 +21,10 @@ jvm_library(
"//java/testFramework",
"//plugins/kotlin/base/plugin",
"//plugins/kotlin/base/plugin:plugin_test_lib",
"//plugins/kotlin/idea",
"//platform/analysis-api:analysis",
"//platform/core-api:core",
"//plugins/kotlin/code-insight/api",
],
runtime_deps = ["//plugins/kotlin/plugin/k1"]
)
@@ -14,5 +14,9 @@
<orderEntry type="module" module-name="intellij.jvm.analysis.testFramework" scope="TEST" />
<orderEntry type="module" module-name="intellij.java.testFramework" scope="TEST" />
<orderEntry type="module" module-name="kotlin.base.plugin" scope="TEST" />
<orderEntry type="module" module-name="kotlin.idea" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.analysis" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.core" scope="TEST" />
<orderEntry type="module" module-name="kotlin.code-insight.api" scope="TEST" />
</component>
</module>
@@ -1,8 +1,11 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.execution.junit.kotlin.codeInspection.deadCode
import com.intellij.codeInspection.InspectionProfileEntry
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode
import org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection
class K1JUnit5ImplicitUsageProviderTest : KotlinJUnit5ImplicitUsageProviderTest() {
override val inspection: InspectionProfileEntry by lazy { UnusedSymbolInspection() }
override val pluginMode: KotlinPluginMode = KotlinPluginMode.K1
}
@@ -21,6 +21,9 @@ jvm_library(
"//plugins/kotlin/base/test:test_test_lib",
"//plugins/kotlin/base/plugin",
"//plugins/kotlin/base/plugin:plugin_test_lib",
"//plugins/kotlin/code-insight/inspections-k2:kotlin-code-insight-inspections-k2",
"//platform/analysis-api:analysis",
"//platform/core-api:core",
],
runtime_deps = ["//plugins/kotlin/plugin/k2"]
)
@@ -14,5 +14,8 @@
<orderEntry type="module" module-name="intellij.jvm.analysis.testFramework" scope="TEST" />
<orderEntry type="module" module-name="kotlin.base.test" scope="TEST" />
<orderEntry type="module" module-name="kotlin.base.plugin" scope="TEST" />
<orderEntry type="module" module-name="kotlin.code-insight.inspections.k2" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.analysis" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.core" scope="TEST" />
</component>
</module>
@@ -1,8 +1,11 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.execution.junit.kotlin.codeInspection.deadCode
import com.intellij.codeInspection.InspectionProfileEntry
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode
import org.jetbrains.kotlin.idea.k2.codeinsight.inspections.UnusedSymbolInspection
class K2JUnit5ImplicitUsageProviderTest : KotlinJUnit5ImplicitUsageProviderTest() {
override val pluginMode: KotlinPluginMode = KotlinPluginMode.K2
override val inspection: InspectionProfileEntry by lazy { UnusedSymbolInspection() }
}
@@ -33,7 +33,9 @@ abstract class KotlinJUnit5ImplicitUsageProviderTest : JUnit5ImplicitUsageProvid
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class MyTest {
@org.junit.jupiter.params.ParameterizedTest(name = "{0}")
fun byName(name: String) { }
fun byName(name: String) {
println(name)
}
}
""".trimIndent())
}
@@ -59,6 +61,28 @@ abstract class KotlinJUnit5ImplicitUsageProviderTest : JUnit5ImplicitUsageProvid
""".trimIndent())
}
fun `test usage of method source with method name`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.util.stream.*
class MyTest {
@org.junit.jupiter.params.ParameterizedTest
@org.junit.jupiter.params.provider.MethodSource("bar")
fun foo(input: String) {
System.out.println(input)
}
companion object {
@JvmStatic
private fun <warning descr="Function \"foo\" is never used">foo</warning>() = Stream.of("")
@JvmStatic
private fun bar() = Stream.of("")
}
}
""".trimIndent())
}
fun `test implicit usage of field source with implicit field name`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.junit.jupiter.params.ParameterizedTest
@@ -79,6 +103,28 @@ abstract class KotlinJUnit5ImplicitUsageProviderTest : JUnit5ImplicitUsageProvid
""".trimIndent())
}
fun `test usage of field source with field name`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.FieldSource
class MyTest {
@ParameterizedTest
@FieldSource("bar")
fun foo(input: String) {
println(input)
}
companion object {
@JvmStatic
val <warning descr="Property \"foo\" is never used">foo</warning> = listOf("a", "b")
@JvmStatic
val bar = listOf("a", "b")
}
}
""".trimIndent())
}
fun `test implicit usage of TempDir as direct annotation`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class Test {
@@ -73,10 +73,10 @@ private fun methodSourceIsImplicitlyUsed(element: PsiMethod): Boolean {
if (parent is PsiClass) psiClass = parent
}
return psiClass.findMethodsByName(methodName, false).any { otherMethod ->
psiMethod != otherMethod
&& MetaAnnotationUtil.isMetaAnnotated(otherMethod, setOf(ORG_JUNIT_JUPITER_PARAMS_PROVIDER_METHOD_SOURCE))
return psiClass.methods.any { otherMethod ->
MetaAnnotationUtil.isMetaAnnotated(otherMethod, setOf(ORG_JUNIT_JUPITER_PARAMS_PROVIDER_METHOD_SOURCE))
&& MetaAnnotationUtil.isMetaAnnotated(otherMethod, setOf(ORG_JUNIT_JUPITER_PARAMS_PARAMETERIZED_TEST))
&& isAnnotationMemberContainsName(methodName, otherMethod, ORG_JUNIT_JUPITER_PARAMS_PROVIDER_METHOD_SOURCE)
}
}
return CachedValuesManager.getCachedValue(element) {
@@ -94,9 +94,10 @@ private fun fieldSourceIsImplicitlyUsed(element: PsiField): Boolean {
if (parent is PsiClass) psiClass = parent
}
return psiClass.findMethodsByName(fieldName, false).any { method ->
return psiClass.methods.any { method ->
MetaAnnotationUtil.isMetaAnnotated(method, setOf(ORG_JUNIT_JUPITER_PARAMS_PROVIDER_FIELD_SOURCE))
&& MetaAnnotationUtil.isMetaAnnotated(method, setOf(ORG_JUNIT_JUPITER_PARAMS_PARAMETERIZED_TEST))
&& isAnnotationMemberContainsName(fieldName, method, ORG_JUNIT_JUPITER_PARAMS_PROVIDER_FIELD_SOURCE)
}
}
@@ -105,6 +106,23 @@ private fun fieldSourceIsImplicitlyUsed(element: PsiField): Boolean {
}
}
private fun isAnnotationMemberContainsName(name: String, method: PsiMethod, annotationFqn: String): Boolean {
val annotation = method.getAnnotation(annotationFqn) ?: return false
val value = annotation.findAttributeValue("value")
if (value == null && method.name == name) return true
if (value is PsiArrayInitializerMemberValue) {
if (value.initializers.isEmpty() && method.name == name) return true
for (memberValue in value.initializers) {
if (memberValue is PsiLiteralExpression) {
val data = JavaConstantExpressionEvaluator.computeConstantExpression(memberValue, null, false) as? String ?: continue
if (data.isEmpty() && method.name == name) return true
if (data == name) return true
}
}
}
return false
}
class JUnit5ImplicitUsageProvider : ImplicitUsageProvider {
override fun isImplicitUsage(element: PsiElement): Boolean {
return (element is PsiParameter && parameterIsUsedByParameterizedTest(element))
@@ -793,6 +793,12 @@ object K2UnusedSymbolUtil {
}
}
}
if (declaration is KtProperty) {
val javaFieldPsi = LightClassUtil.getLightClassBackingField(declaration)
if (javaFieldPsi != null && isJavaEntryPoint.isEntryPoint(javaFieldPsi)) {
return true
}
}
// can't rely on a light element, check annotation ourselves
val entryPointsManager = EntryPointsManager.getInstance(declaration.project) as EntryPointsManagerBase
return checkAnnotatedUsingPatterns(
@@ -126,6 +126,12 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
}
}
}
if (declaration is KtProperty) {
val javaFieldPsi = LightClassUtil.getLightClassBackingField(declaration)
if (javaFieldPsi != null && javaInspection.isEntryPoint(javaFieldPsi)) {
return true
}
}
// can't rely on light element, check annotation ourselves
val entryPointsManager = EntryPointsManager.getInstance(declaration.project) as EntryPointsManagerBase
return checkAnnotatedUsingPatterns(