mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
NonDefaultConstructorInspection — check all referenced from extension registration classes (part 2)
This commit is contained in:
@@ -10,15 +10,19 @@ import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiClassType
|
||||
import com.intellij.psi.PsiParameterList
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import com.intellij.psi.xml.XmlTag
|
||||
import com.intellij.util.SmartList
|
||||
import gnu.trove.THashSet
|
||||
import org.jetbrains.idea.devkit.util.processExtensionsByClassName
|
||||
import org.jetbrains.uast.UClass
|
||||
import org.jetbrains.uast.UMethod
|
||||
import org.jetbrains.uast.convert
|
||||
import org.jetbrains.uast.getLanguagePlugin
|
||||
|
||||
internal class NonDefaultConstructorInspection : DevKitUastInspectionBase() {
|
||||
override fun checkClass(aClass: UClass, manager: InspectionManager, isOnTheFly: Boolean): Array<ProblemDescriptor>? {
|
||||
val javaPsi = aClass.javaPsi
|
||||
if (!javaPsi.isPhysical || javaPsi.classKind != JvmClassKind.CLASS ||
|
||||
if (javaPsi.classKind != JvmClassKind.CLASS ||
|
||||
PsiUtil.isInnerClass(javaPsi) || PsiUtil.isLocalOrAnonymousClass(javaPsi) ||
|
||||
PsiUtil.isAbstractClass(javaPsi)) {
|
||||
return null
|
||||
@@ -39,7 +43,7 @@ internal class NonDefaultConstructorInspection : DevKitUastInspectionBase() {
|
||||
}
|
||||
|
||||
var errors: MutableList<ProblemDescriptor>? = null
|
||||
for (method in constructors) {
|
||||
loop@ for (method in constructors) {
|
||||
val parameters = method.parameterList
|
||||
if (parameters.isEmpty || isAllowedParameters(parameters)) {
|
||||
// allow to have empty constructor and extra (e.g. DartQuickAssistIntention)
|
||||
@@ -49,7 +53,13 @@ internal class NonDefaultConstructorInspection : DevKitUastInspectionBase() {
|
||||
if (errors == null) {
|
||||
errors = SmartList()
|
||||
}
|
||||
errors.add(manager.createProblemDescriptor(method ?: continue,
|
||||
|
||||
// kotlin is not physical, but here only physical is expected, so, convert to uast element and use sourcePsi
|
||||
val anchorElement = when {
|
||||
method.isPhysical -> method
|
||||
else -> aClass.getLanguagePlugin().convert<UMethod>(method, aClass).sourcePsi ?: continue@loop
|
||||
}
|
||||
errors.add(manager.createProblemDescriptor(anchorElement,
|
||||
"Bean extension class should not have constructor with parameters", true,
|
||||
ProblemHighlightType.ERROR, isOnTheFly))
|
||||
}
|
||||
@@ -60,8 +70,14 @@ internal class NonDefaultConstructorInspection : DevKitUastInspectionBase() {
|
||||
// cannot check com.intellij.codeInsight.intention.IntentionAction by class qualified name because not all IntentionAction used as IntentionActionBean
|
||||
private fun isReferencedByExtension(clazz: UClass, project: Project): Boolean {
|
||||
var isFound = false
|
||||
processExtensionsByClassName(project, clazz.qualifiedName ?: return false) { tag ->
|
||||
if (tag.name == "className" || tag.subTags.any { it.name == "className" } || tag.attributes.any { it.name.startsWith("implementation") }) {
|
||||
val qualifiedNamed = clazz.qualifiedName ?: return false
|
||||
processExtensionsByClassName(project, qualifiedNamed) { tag, point ->
|
||||
// check only bean extensions
|
||||
if (point.beanClass.value == null) {
|
||||
return@processExtensionsByClassName true
|
||||
}
|
||||
|
||||
if (tag.name == "className" || tag.subTags.any { it.name == "className" } || checkAttributes(tag, qualifiedNamed)) {
|
||||
isFound = true
|
||||
}
|
||||
!isFound
|
||||
@@ -69,6 +85,21 @@ private fun isReferencedByExtension(clazz: UClass, project: Project): Boolean {
|
||||
return isFound
|
||||
}
|
||||
|
||||
// problem - tag
|
||||
//<lang.elementManipulator forClass="com.intellij.psi.css.impl.CssTokenImpl"
|
||||
// implementationClass="com.intellij.psi.css.impl.CssTokenImpl$Manipulator"/>
|
||||
// will be found for `com.intellij.psi.css.impl.CssTokenImpl`, but we need to ignore `forClass` and check that we have exact match for implementation attribute
|
||||
private fun checkAttributes(tag: XmlTag, qualifiedNamed: String): Boolean {
|
||||
if (tag.name == "modelFacade") {
|
||||
// DbmsExtension passes Dbms instance directly, doesn't need to check
|
||||
return false
|
||||
}
|
||||
|
||||
return tag.attributes.any {
|
||||
it.name.startsWith("implementation") && it.value == qualifiedNamed
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAllowedParameters(list: PsiParameterList): Boolean {
|
||||
if (list.parametersCount != 1) {
|
||||
return false
|
||||
|
||||
@@ -59,10 +59,10 @@ private fun findEpCandidates(project: Project, clazz: JvmClass): Collection<Exte
|
||||
val name = clazz.name ?: return emptyList()
|
||||
val result = SmartList<ExtensionCandidate>()
|
||||
val smartPointerManager by lazy { SmartPointerManager.getInstance(project) }
|
||||
processExtensionsByClassName(project, name) {
|
||||
val forClass = it.getAttributeValue("forClass")
|
||||
processExtensionsByClassName(project, name) { tag, _ ->
|
||||
val forClass = tag.getAttributeValue("forClass")
|
||||
if (forClass == null || !forClass.contains(name)) {
|
||||
result.add(ExtensionCandidate(smartPointerManager.createSmartPsiElementPointer(it)))
|
||||
result.add(ExtensionCandidate(smartPointerManager.createSmartPsiElementPointer(tag)))
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
@@ -55,20 +55,21 @@ private fun processExtensionDeclarations(name: String, project: Project, strictM
|
||||
private fun findExtensionsByClassName(project: Project, className: String): List<ExtensionCandidate> {
|
||||
val result = SmartList<ExtensionCandidate>()
|
||||
val smartPointerManager by lazy { SmartPointerManager.getInstance(project) }
|
||||
processExtensionsByClassName(project, className) {
|
||||
result.add(ExtensionCandidate(smartPointerManager.createSmartPsiElementPointer(it)))
|
||||
processExtensionsByClassName(project, className) { tag, _ ->
|
||||
result.add(ExtensionCandidate(smartPointerManager.createSmartPsiElementPointer(tag)))
|
||||
true
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal inline fun processExtensionsByClassName(project: Project, className: String, crossinline processor: (XmlTag) -> Boolean) {
|
||||
internal inline fun processExtensionsByClassName(project: Project, className: String, crossinline processor: (XmlTag, ExtensionPoint) -> Boolean) {
|
||||
processExtensionDeclarations(className, project, true) { extension, tag ->
|
||||
if (extension.extensionPoint == null) {
|
||||
val point = extension.extensionPoint
|
||||
if (point == null) {
|
||||
true
|
||||
}
|
||||
else {
|
||||
processor(tag)
|
||||
processor(tag, point)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user