diff --git a/jvm/jvm-analysis-testFramework/src/com/intellij/jvm/analysis/testFramework/JvmImplicitUsageProviderTestBase.kt b/jvm/jvm-analysis-testFramework/src/com/intellij/jvm/analysis/testFramework/JvmImplicitUsageProviderTestBase.kt index ffff0d652b51..6afa52c04fee 100644 --- a/jvm/jvm-analysis-testFramework/src/com/intellij/jvm/analysis/testFramework/JvmImplicitUsageProviderTestBase.kt +++ b/jvm/jvm-analysis-testFramework/src/com/intellij/jvm/analysis/testFramework/JvmImplicitUsageProviderTestBase.kt @@ -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) } } \ No newline at end of file diff --git a/plugins/junit/java-tests/test/com/intellij/execution/junit/codeInspection/deadCode/JavaJunit5ImplicitUsageProviderTest.kt b/plugins/junit/java-tests/test/com/intellij/execution/junit/codeInspection/deadCode/JavaJunit5ImplicitUsageProviderTest.kt index 3dafdaf28d2d..cb85e0e15937 100644 --- a/plugins/junit/java-tests/test/com/intellij/execution/junit/codeInspection/deadCode/JavaJunit5ImplicitUsageProviderTest.kt +++ b/plugins/junit/java-tests/test/com/intellij/execution/junit/codeInspection/deadCode/JavaJunit5ImplicitUsageProviderTest.kt @@ -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 foo() { + return Stream.of(""); + } + + private static Stream 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 foo = Stream.of(""); + private static Stream bar = Stream.of(""); + } + """.trimIndent()) + } + fun `test implicit usage of TempDir as direct annotation`() { myFixture.testHighlighting(JvmLanguage.JAVA, """ class Test { diff --git a/plugins/junit/kotlin-tests-k1/BUILD.bazel b/plugins/junit/kotlin-tests-k1/BUILD.bazel index 2c77b0531284..2385d5ede5e2 100644 --- a/plugins/junit/kotlin-tests-k1/BUILD.bazel +++ b/plugins/junit/kotlin-tests-k1/BUILD.bazel @@ -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"] ) diff --git a/plugins/junit/kotlin-tests-k1/intellij.junit.kotlin.tests.k1.iml b/plugins/junit/kotlin-tests-k1/intellij.junit.kotlin.tests.k1.iml index 8aed4ad00867..d45083999343 100644 --- a/plugins/junit/kotlin-tests-k1/intellij.junit.kotlin.tests.k1.iml +++ b/plugins/junit/kotlin-tests-k1/intellij.junit.kotlin.tests.k1.iml @@ -14,5 +14,9 @@ + + + + \ No newline at end of file diff --git a/plugins/junit/kotlin-tests-k1/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/K1JUnit5ImplicitUsageProviderTest.kt b/plugins/junit/kotlin-tests-k1/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/K1JUnit5ImplicitUsageProviderTest.kt index 898eaab1c950..db9972cf97b9 100644 --- a/plugins/junit/kotlin-tests-k1/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/K1JUnit5ImplicitUsageProviderTest.kt +++ b/plugins/junit/kotlin-tests-k1/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/K1JUnit5ImplicitUsageProviderTest.kt @@ -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 } \ No newline at end of file diff --git a/plugins/junit/kotlin-tests-k2/BUILD.bazel b/plugins/junit/kotlin-tests-k2/BUILD.bazel index afa9cbe194bf..4d6287ca26f6 100644 --- a/plugins/junit/kotlin-tests-k2/BUILD.bazel +++ b/plugins/junit/kotlin-tests-k2/BUILD.bazel @@ -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"] ) diff --git a/plugins/junit/kotlin-tests-k2/intellij.junit.kotlin.tests.k2.iml b/plugins/junit/kotlin-tests-k2/intellij.junit.kotlin.tests.k2.iml index e31689c76e68..6cfdd8cb4247 100644 --- a/plugins/junit/kotlin-tests-k2/intellij.junit.kotlin.tests.k2.iml +++ b/plugins/junit/kotlin-tests-k2/intellij.junit.kotlin.tests.k2.iml @@ -14,5 +14,8 @@ + + + \ No newline at end of file diff --git a/plugins/junit/kotlin-tests-k2/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/K2JUnit5ImplicitUsageProviderTest.kt b/plugins/junit/kotlin-tests-k2/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/K2JUnit5ImplicitUsageProviderTest.kt index 9b00a1d3e75d..5fd90ad28312 100644 --- a/plugins/junit/kotlin-tests-k2/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/K2JUnit5ImplicitUsageProviderTest.kt +++ b/plugins/junit/kotlin-tests-k2/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/K2JUnit5ImplicitUsageProviderTest.kt @@ -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() } } \ No newline at end of file diff --git a/plugins/junit/kotlin-tests-shared/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/KotlinJUnit5ImplicitUsageProviderTest.kt b/plugins/junit/kotlin-tests-shared/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/KotlinJUnit5ImplicitUsageProviderTest.kt index 28ed501a834a..c5e93aae0554 100644 --- a/plugins/junit/kotlin-tests-shared/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/KotlinJUnit5ImplicitUsageProviderTest.kt +++ b/plugins/junit/kotlin-tests-shared/test/com/intellij/execution/junit/kotlin/codeInspection/deadCode/KotlinJUnit5ImplicitUsageProviderTest.kt @@ -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 foo() = 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 foo = 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 { diff --git a/plugins/junit/src/com/intellij/execution/junit/codeInspection/deadCode/JUnit5ImplicitUsageProvider.kt b/plugins/junit/src/com/intellij/execution/junit/codeInspection/deadCode/JUnit5ImplicitUsageProvider.kt index d07a8b2b8ea9..95d141839ac5 100644 --- a/plugins/junit/src/com/intellij/execution/junit/codeInspection/deadCode/JUnit5ImplicitUsageProvider.kt +++ b/plugins/junit/src/com/intellij/execution/junit/codeInspection/deadCode/JUnit5ImplicitUsageProvider.kt @@ -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)) diff --git a/plugins/kotlin/highlighting/highlighting-k2/src/org/jetbrains/kotlin/idea/highlighting/K2UnusedSymbolUtil.kt b/plugins/kotlin/highlighting/highlighting-k2/src/org/jetbrains/kotlin/idea/highlighting/K2UnusedSymbolUtil.kt index 086711afcd60..26b1e5f085f3 100644 --- a/plugins/kotlin/highlighting/highlighting-k2/src/org/jetbrains/kotlin/idea/highlighting/K2UnusedSymbolUtil.kt +++ b/plugins/kotlin/highlighting/highlighting-k2/src/org/jetbrains/kotlin/idea/highlighting/K2UnusedSymbolUtil.kt @@ -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( diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt index b164de9e359f..6ef77efd122a 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt @@ -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(