[devkit] Do not report service classes that contain a check on the unit test or UI-less mode

IJ-CR-105402

GitOrigin-RevId: cc3e83f7e6e6f93f663c43aea66a3680e8c264b4
This commit is contained in:
Andrey Cherkasov
2023-06-18 18:33:31 +00:00
committed by intellij-monorepo-bot
parent 7edae307bd
commit f96d25fe8d
10 changed files with 100 additions and 1 deletions
@@ -33,7 +33,7 @@ internal class LightServiceMigrationCodeInspection : DevKitUastInspectionBase(UC
JvmInheritanceUtil.isInheritor(aClass, PersistentStateComponent::class.java.canonicalName)) {
continue
}
if (serviceImplementation == psiClass) {
if (serviceImplementation == psiClass && !containsUnitTestOrHeadlessModeCheck(aClass)) {
return registerProblem(aClass, level, manager, isOnTheFly)
}
}
@@ -4,18 +4,23 @@
package org.jetbrains.idea.devkit.inspections
import com.intellij.openapi.application.Application
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.ServiceDescriptor
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.psi.PsiClass
import com.intellij.util.xml.DomElement
import com.intellij.util.xml.DomUtil
import com.siyeh.ig.callMatcher.CallMatcher
import org.jetbrains.annotations.Nls
import org.jetbrains.idea.devkit.DevKitBundle
import org.jetbrains.idea.devkit.dom.Extension
import org.jetbrains.idea.devkit.util.DevKitDomUtil
import org.jetbrains.idea.devkit.util.PluginPlatformInfo
import org.jetbrains.idea.devkit.util.PsiUtil
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UClass
import org.jetbrains.uast.visitor.AbstractUastVisitor
internal data class ServiceInfo(val aClass: PsiClass, val level: Service.Level)
@@ -64,4 +69,25 @@ internal fun isVersion193OrHigher(aClass: PsiClass): Boolean {
return buildNumber != null && buildNumber.baselineVersion >= 193
}
internal fun containsUnitTestOrHeadlessModeCheck(aClass: UClass): Boolean {
var result = false
aClass.accept(object : AbstractUastVisitor() {
override fun visitCallExpression(node: UCallExpression): Boolean {
if (IS_UNIT_TEST_OR_HEADLESS_MODE.uCallMatches(node)) {
result = true
return false
}
return super.visitCallExpression(node)
}
})
return result
}
private val disallowedAttributes = setOf("serviceInterface", "os", "client", "overrides", "id", "preload")
private val IS_UNIT_TEST_OR_HEADLESS_MODE = CallMatcher.anyOf(
CallMatcher.instanceCall(Application::class.java.canonicalName, "isUnitTestMode", "isHeadlessEnvironment"),
CallMatcher.staticCall("org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt", "isUnitTestMode", "isHeadlessEnvironment"),
)
@@ -15,6 +15,8 @@ import com.intellij.util.xml.highlighting.DomElementAnnotationHolder
import com.intellij.util.xml.highlighting.DomHighlightingHelper
import org.jetbrains.idea.devkit.DevKitBundle
import org.jetbrains.idea.devkit.dom.Extension
import org.jetbrains.uast.UClass
import org.jetbrains.uast.toUElement
internal class LightServiceMigrationXMLInspection : DevKitPluginXmlInspectionBase() {
@@ -30,6 +32,8 @@ internal class LightServiceMigrationXMLInspection : DevKitPluginXmlInspectionBas
JvmInheritanceUtil.isInheritor(aClass, PersistentStateComponent::class.java.canonicalName)) {
return
}
val uClass = aClass.toUElement(UClass::class.java)
if (uClass == null || containsUnitTestOrHeadlessModeCheck(uClass)) return
if (aClass.hasAnnotation(Service::class.java.canonicalName)) {
val message = DevKitBundle.message("inspection.light.service.migration.already.annotated.message")
holder.createProblem(element, ProblemHighlightType.ERROR, message, null)
@@ -0,0 +1,10 @@
package com.example.demo;
import com.intellij.openapi.application.ApplicationManager;
final class MyService {
public void foo() {
if (ApplicationManager.getApplication().isHeadlessEnvironment()) {
}
}
}
@@ -0,0 +1,7 @@
package com.example.demo;
import com.intellij.openapi.application.ApplicationManager;
final class MyService {
private final static boolean isUnitTestMode = ApplicationManager.getApplication().isUnitTestMode();
}
@@ -44,4 +44,12 @@ internal class LightServiceMigrationInspectionTest : LightServiceMigrationInspec
fun testLibraryClass() {
myFixture.testHighlighting(getTestName(true) + ".xml")
}
fun testUnitTestMode() {
doTest(getCodeFilePath(), CANNOT_BE_LIGHT_SERVICE_XML)
}
fun testHeadlessEnvironment() {
doTest(getCodeFilePath(), CANNOT_BE_LIGHT_SERVICE_XML)
}
}
@@ -0,0 +1,10 @@
package com.example.demo
import com.intellij.openapi.application.ApplicationManager
class MyService {
fun foo() {
if (ApplicationManager.getApplication().isHeadlessEnvironment()) {
}
}
}
@@ -0,0 +1,5 @@
package com.example.demo
import com.intellij.openapi.application.ApplicationManager
class MyService(val isUnitTestMode: Boolean = ApplicationManager.getApplication().isUnitTestMode)
@@ -41,4 +41,12 @@ internal class KtLightServiceMigrationInspectionTest : LightServiceMigrationInsp
fun testLightService() {
doTest(getCodeFilePath(), getXmlFilePath())
}
fun testUnitTestMode() {
doTest(getCodeFilePath(), CANNOT_BE_LIGHT_SERVICE_XML)
}
fun testHeadlessEnvironment() {
doTest(getCodeFilePath(), CANNOT_BE_LIGHT_SERVICE_XML)
}
}
@@ -39,6 +39,27 @@ abstract class LightServiceMigrationInspectionTestBase : LightDevKitInspectionFi
public @interface Service {}
"""
)
myFixture.addClass("""
package com.intellij.openapi.application;
public final class ApplicationManager {
private static Application ourApplication;
public static Application getApplication() {
return ourApplication;
}
}
""")
myFixture.addClass("""
package com.intellij.openapi.application;
public interface Application {
boolean isUnitTestMode();
boolean isHeadlessEnvironment();
}
""")
myFixture.enableInspections(LightServiceMigrationXMLInspection::class.java,
LightServiceMigrationCodeInspection::class.java)
}