[devKit] RetrievingServiceInspection reports problems not only when retrieving light services but also regular ones

IJ-CR-102194

GitOrigin-RevId: e25dd9f51149452c18bcd9b33859660a9eff9aa5
This commit is contained in:
Andrey Cherkasov
2023-03-13 12:53:45 +00:00
committed by intellij-monorepo-bot
parent ba1b002ee6
commit c9a55f3ac5
15 changed files with 46 additions and 19 deletions
@@ -1,6 +1,6 @@
<html>
<body>
Reports problems when retrieving a light service.
Reports problems when retrieving services.
<p>The following problems are reported:</p>
<ul>
<li>Mismatch when retrieving a service: attempting to get a project-level service as an application-level service, or vice versa.</li>
@@ -239,10 +239,10 @@
enabledByDefault="false" level="WARNING"
implementationClass="org.jetbrains.idea.devkit.inspections.MismatchedLightServiceLevelAndCtorInspection"
key="inspection.mismatched.light.service.level.and.ctor.display.name"/>
<localInspection language="UAST" shortName="RetrievingLightService"
<localInspection language="UAST" shortName="RetrievingService"
groupPathKey="inspections.group.path" groupKey="inspections.group.code"
enabledByDefault="false" level="WARNING"
implementationClass="org.jetbrains.idea.devkit.inspections.RetrievingLightServiceInspection"
implementationClass="org.jetbrains.idea.devkit.inspections.RetrievingServiceInspection"
key="inspection.retrieving.light.service.display.name"/>
<localInspection language="UAST" shortName="NonFinalOrNonInternalExtensionClass"
groupPathKey="inspections.group.path" groupKey="inspections.group.code"
@@ -621,7 +621,7 @@ inspection.mismatched.light.service.level.and.ctor.display.name=Mismatch between
inspection.mismatched.light.service.level.and.ctor.project.level.required=If constructor takes Project, Service.Level.PROJECT is required
inspection.mismatched.light.service.level.and.ctor.app.level.ctor.required=Application level service requires no-arg constructor or constructor taking Coroutine
inspection.retrieving.light.service.display.name=Problems when retrieving a light service
inspection.retrieving.light.service.display.name=Problems when retrieving services
inspection.retrieving.light.service.mismatch.for.project.level=The project-level service is retrieved as an application-level service
inspection.retrieving.light.service.mismatch.for.app.level=The application-level service is retrieved as a project-level service
inspection.retrieving.light.service.can.be.replaced.with=Can be replaced with ''{0}.{1}()'' call
@@ -8,19 +8,23 @@ import com.intellij.codeInspection.isInheritorOf
import com.intellij.openapi.application.Application
import com.intellij.openapi.components.ComponentManager
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.ServiceDescriptor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
import com.intellij.uast.UastHintedVisitorAdapter
import com.intellij.util.xml.DomManager
import com.siyeh.ig.callMatcher.CallMatcher
import org.jetbrains.annotations.PropertyKey
import org.jetbrains.idea.devkit.DevKitBundle
import org.jetbrains.idea.devkit.dom.Extension
import org.jetbrains.idea.devkit.util.locateExtensionsByPsiClass
import org.jetbrains.uast.*
import org.jetbrains.uast.generate.UastCodeGenerationPlugin
import org.jetbrains.uast.generate.replace
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
internal class RetrievingLightServiceInspection : DevKitUastInspectionBase() {
internal class RetrievingServiceInspection : DevKitUastInspectionBase() {
private val COMPONENT_MANAGER_FQN = ComponentManager::class.java.canonicalName
private val COMPONENT_MANAGER_GET_SERVICE: CallMatcher = CallMatcher.anyOf(
CallMatcher.instanceCall(COMPONENT_MANAGER_FQN, "getService").parameterTypes(CommonClassNames.JAVA_LANG_CLASS),
@@ -33,11 +37,11 @@ internal class RetrievingLightServiceInspection : DevKitUastInspectionBase() {
if (!COMPONENT_MANAGER_GET_SERVICE.uCallMatches(node.selector as? UCallExpression)) return true
val uClass = (node.selector.getExpressionType() as? PsiClassType)?.resolve()?.toUElement(UClass::class.java) ?: return true
if (isInsideGetInstance(node, uClass)) return true
val serviceAnnotation = uClass.findAnnotation(Service::class.java.canonicalName) ?: return true
val level = getLevel(serviceAnnotation)
val level = getLevel(uClass, holder.project) ?: return true
val receiverType = node.receiver.getExpressionType() ?: return true
val array = listOf(
MismatchReceivingChecker(Project::class.java.canonicalName, Level.APP, "inspection.retrieving.light.service.mismatch.for.app.level"),
MismatchReceivingChecker(Project::class.java.canonicalName, Level.APP,
"inspection.retrieving.light.service.mismatch.for.app.level"),
MismatchReceivingChecker(Application::class.java.canonicalName, Level.PROJECT,
"inspection.retrieving.light.service.mismatch.for.project.level"))
val hasError = array.any { it.check(level, receiverType, toHighlight, holder) }
@@ -46,6 +50,29 @@ internal class RetrievingLightServiceInspection : DevKitUastInspectionBase() {
}
}, arrayOf(UQualifiedReferenceExpression::class.java))
private fun getLevel(uClass: UClass, project: Project): Level? {
val serviceAnnotation = uClass.findAnnotation(Service::class.java.canonicalName)
if (serviceAnnotation != null) return getLevel(serviceAnnotation)
val javaPsi = uClass.javaPsi
val domManager = DomManager.getDomManager(project)
for (candidate in locateExtensionsByPsiClass(javaPsi)) {
val tag = candidate.pointer.element ?: continue
val element = domManager.getDomElement(tag) ?: continue
if (element is Extension && hasServiceBeanFqn(element)) {
return when (element.extensionPoint?.name?.value) {
"applicationService" -> Level.APP
"projectService" -> Level.PROJECT
else -> Level.NOT_SPECIFIED
}
}
}
return null
}
private fun hasServiceBeanFqn(extension: Extension): Boolean {
return extension.extensionPoint?.beanClass?.stringValue == ServiceDescriptor::class.java.canonicalName
}
data class MismatchReceivingChecker(val retrievingClassName: String,
val level: Level,
val mismatchRetrievingKey: @PropertyKey(resourceBundle = DevKitBundle.BUNDLE) String) {
@@ -3,12 +3,12 @@ package org.jetbrains.idea.devkit.inspections
import com.intellij.testFramework.TestDataPath
import org.jetbrains.idea.devkit.DevkitJavaTestsUtil
import org.jetbrains.idea.devkit.inspections.quickfix.RetrievingLightServiceInspectionTestBase
import org.jetbrains.idea.devkit.inspections.quickfix.RetrievingServiceInspectionTestBase
@TestDataPath("\$CONTENT_ROOT/testData/inspections/retrievingLightService")
class RetrievingLightServiceInspectionTest : RetrievingLightServiceInspectionTestBase() {
@TestDataPath("\$CONTENT_ROOT/testData/inspections/retrievingService")
class RetrievingServiceInspectionTest : RetrievingServiceInspectionTestBase() {
override fun getBasePath() = DevkitJavaTestsUtil.TESTDATA_PATH + "inspections/retrievingLightService/"
override fun getBasePath() = DevkitJavaTestsUtil.TESTDATA_PATH + "inspections/retrievingService/"
override fun getFileExtension() = "java"
@@ -2,13 +2,13 @@
package org.jetbrains.idea.devkit.kotlin.inspections
import com.intellij.testFramework.TestDataPath
import org.jetbrains.idea.devkit.inspections.quickfix.RetrievingLightServiceInspectionTestBase
import org.jetbrains.idea.devkit.inspections.quickfix.RetrievingServiceInspectionTestBase
import org.jetbrains.idea.devkit.kotlin.DevkitKtTestsUtil
@TestDataPath("\$CONTENT_ROOT/testData/inspections/retrievingLightService")
class KtRetrievingLightServiceInspectionTest : RetrievingLightServiceInspectionTestBase() {
@TestDataPath("\$CONTENT_ROOT/testData/inspections/retrievingService")
class KtRetrievingServiceInspectionTest : RetrievingServiceInspectionTestBase() {
override fun getBasePath() = DevkitKtTestsUtil.TESTDATA_PATH + "inspections/retrievingLightService/"
override fun getBasePath() = DevkitKtTestsUtil.TESTDATA_PATH + "inspections/retrievingService/"
override fun getFileExtension() = "kt"
@@ -1,13 +1,13 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.devkit.inspections.quickfix
import org.jetbrains.idea.devkit.inspections.RetrievingLightServiceInspection
import org.jetbrains.idea.devkit.inspections.RetrievingServiceInspection
abstract class RetrievingLightServiceInspectionTestBase : LightDevKitInspectionFixTestBase() {
abstract class RetrievingServiceInspectionTestBase : DevKitInspectionFixTestBase() {
override fun setUp() {
super.setUp()
myFixture.enableInspections(RetrievingLightServiceInspection())
myFixture.enableInspections(RetrievingServiceInspection())
myFixture.addClass("""
package com.intellij.openapi.components;