[devkit] IDEA-294395 filter out services mentioned in extensions' attributes

GitOrigin-RevId: 5f55e2176907143ae07bb828ac11222ab5f1b3ba
This commit is contained in:
Elena Lyulina
2023-03-31 11:18:01 +02:00
committed by intellij-monorepo-bot
parent 271c7f2045
commit 8a21dc30d6
11 changed files with 82 additions and 2 deletions

View File

@@ -5,6 +5,6 @@ Reports extension implementation being additionally registered as a service/comp
While there can be multiple extension instances, the IntelliJ Platform ensures that only one instance of a service/component is loaded,
thus registering the same class as both an extension and a service/component may cause issues.
</p>
<p><small>New in 2023.1</small>
<p><small>New in 2023.2</small>
</body>
</html>

View File

@@ -8,6 +8,7 @@ import com.intellij.openapi.components.Service
import com.intellij.openapi.components.ServiceDescriptor
import com.intellij.psi.PsiClass
import com.intellij.psi.util.InheritanceUtil
import com.intellij.psi.xml.XmlTag
import com.intellij.util.xml.DomManager
import org.jetbrains.idea.devkit.DevKitBundle
import org.jetbrains.idea.devkit.dom.Extension
@@ -17,6 +18,8 @@ import org.jetbrains.uast.UClass
class ExtensionRegisteredAsServiceOrComponentInspection : DevKitUastInspectionBase(UClass::class.java) {
private val serviceAttributeNames = setOf("service")
override fun checkClass(uClass: UClass, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor?>? {
val psiClass = uClass.javaPsi
if (!PsiUtil.isExtensionPointImplementationCandidate(psiClass)) {
@@ -36,7 +39,7 @@ class ExtensionRegisteredAsServiceOrComponentInspection : DevKitUastInspectionBa
if (element is Extension) {
if (hasServiceBeanFqn(element)) {
isService = true
} else {
} else if (!isValueOfServiceAttribute(tag, psiClass.qualifiedName)) {
isExtension = true
}
}
@@ -61,6 +64,14 @@ class ExtensionRegisteredAsServiceOrComponentInspection : DevKitUastInspectionBa
return extension.extensionPoint?.beanClass?.stringValue == ServiceDescriptor::class.java.canonicalName
}
/**
* Finds all attribute names with a given value and checks they all are among [serviceAttributeNames].
*/
private fun isValueOfServiceAttribute(tag: XmlTag, value: String?): Boolean {
val attributeNames = tag.attributes.filter { it.value == value }.map { it.name }.toSet()
return serviceAttributeNames.containsAll(attributeNames)
}
private fun isLightService(uClass: UClass): Boolean {
return uClass.findAnnotation(Service::class.java.canonicalName) != null
}

View File

@@ -0,0 +1,9 @@
import com.intellij.util.xmlb.annotations.Attribute
class ServiceAttributeBean {
@Attribute("id")
String id = ""
@Attribute("service")
String service = ""
}

View File

@@ -0,0 +1,2 @@
// registered as a service + mentioned in extension registration as a service
class ServiceWithServiceAttribute { }

View File

@@ -0,0 +1,14 @@
<idea-plugin>
<id>com.intellij.example</id>
<extensionPoints>
<extensionPoint name="myExtensionWithServiceAttribute" beanClass="ServiceAttributeBean" dynamic="true"/>
<extensionPoint name="projectService" beanClass="com.intellij.openapi.components.ServiceDescriptor" dynamic="true"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij.example">
<projectService serviceImplementation="ServiceWithServiceAttribute"/>
<myExtensionWithServiceAttribute id="my.extension.with.service.attribute.id" service="ServiceWithServiceAttribute"/>
</extensions>
</idea-plugin>

View File

@@ -39,4 +39,9 @@ class ExtensionRegisteredAsServiceOrComponentInspectionTest : ExtensionRegistere
setPluginXml("extension-component-plugin.xml")
doTest()
}
fun testServiceWithServiceAttribute() {
setPluginXml("service-attribute-plugin.xml")
myFixture.testHighlighting("ServiceWithServiceAttribute.java", "ServiceAttributeBean.java")
}
}

View File

@@ -0,0 +1,9 @@
import com.intellij.util.xmlb.annotations.Attribute
class ServiceAttributeBean {
@Attribute("id")
val id: String = ""
@Attribute("service")
val service: String = ""
}

View File

@@ -0,0 +1,2 @@
// registered as a service + mentioned in extension registration as a service
class ServiceWithServiceAttribute { }

View File

@@ -0,0 +1,14 @@
<idea-plugin>
<id>com.intellij.example</id>
<extensionPoints>
<extensionPoint name="myExtensionWithServiceAttribute" beanClass="ServiceAttributeBean" dynamic="true"/>
<extensionPoint name="projectService" beanClass="com.intellij.openapi.components.ServiceDescriptor" dynamic="true"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij.example">
<projectService serviceImplementation="ServiceWithServiceAttribute"/>
<myExtensionWithServiceAttribute id="my.extension.with.service.attribute.id" service="ServiceWithServiceAttribute"/>
</extensions>
</idea-plugin>

View File

@@ -41,4 +41,9 @@ class KtExtensionRegisteredAsServiceOrComponentInspectionTest : ExtensionRegiste
setPluginXml("extension-component-plugin.xml")
doTest()
}
fun testServiceWithServiceAttribute() {
setPluginXml("service-attribute-plugin.xml")
myFixture.testHighlighting("ServiceWithServiceAttribute.kt", "ServiceAttributeBean.kt")
}
}

View File

@@ -49,6 +49,15 @@ abstract class ExtensionRegisteredAsServiceOrComponentInspectionTestBase : Plugi
public interface BaseComponent { }
""".trimIndent()
)
myFixture.addClass(
//language=java
"""
package com.intellij.util.xmlb.annotations;
public @interface Attribute { }
""".trimIndent()
)
}
protected open fun doTest() {