mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[devkit] Implement Go to implementation for @Remote methods and find usages in tests for production methods
GitOrigin-RevId: a3b28d0f93b8aa807f8b2b3f459ab56d69e49203
This commit is contained in:
committed by
intellij-monorepo-bot
parent
dcd7792c72
commit
8e9ccf6091
@@ -60,9 +60,9 @@
|
||||
<fileBasedIndex implementation="org.jetbrains.idea.devkit.dom.index.PluginIdDependenciesIndex"/>
|
||||
<fileBasedIndex implementation="org.jetbrains.idea.devkit.dom.index.ExtensionPointIndex"/>
|
||||
<fileBasedIndex implementation="org.jetbrains.idea.devkit.dom.index.ExtensionPointClassIndex"/>
|
||||
|
||||
|
||||
<dom.fileMetaData implementation="org.jetbrains.idea.devkit.dom.impl.productModules.ProductModulesDomFileDescription"
|
||||
rootTagName="product-modules" />
|
||||
rootTagName="product-modules"/>
|
||||
<dom.fileMetaData implementation="org.jetbrains.idea.devkit.dom.liveTemplates.TemplateSetDescription" rootTagName="templateSet"/>
|
||||
|
||||
<editorTabTitleProvider implementation="org.jetbrains.idea.devkit.dom.ide.PluginDescriptorEditorTabTitleProvider"/>
|
||||
@@ -86,6 +86,13 @@
|
||||
<completion.contributor implementationClass="org.jetbrains.idea.devkit.completion.ExtensionOrderKeywordCompletionContributor"
|
||||
language="XML"/>
|
||||
|
||||
<useScopeEnlarger implementation="org.jetbrains.idea.devkit.driver.RemoteUseScopeEnlarger"/>
|
||||
<methodReferencesSearch implementation="org.jetbrains.idea.devkit.driver.RemoteMethodReferenceSearcher"/>
|
||||
<overridingMethodsSearch implementation="org.jetbrains.idea.devkit.driver.RemoteMethodImplementationSearcher"/>
|
||||
<allOverridingMethodsSearch implementation="org.jetbrains.idea.devkit.driver.RemoteMethodAllImplementationSearcher"/>
|
||||
<directClassInheritorsSearch implementation="org.jetbrains.idea.devkit.driver.RemoteInterfaceDirectImplementationSearcher"/>
|
||||
<classInheritorsSearch implementation="org.jetbrains.idea.devkit.driver.RemoteInterfaceImplementationSearcher"/>
|
||||
|
||||
<localInspection language="XML" shortName="PluginXmlValidity" applyToDialects="false"
|
||||
projectType="INTELLIJ_PLUGIN"
|
||||
groupPathKey="inspections.group.path" groupKey="inspections.group.descriptor"
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.driver
|
||||
|
||||
import com.intellij.lang.jvm.JvmModifier
|
||||
import com.intellij.openapi.application.ReadAction
|
||||
import com.intellij.openapi.util.Couple
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.search.searches.AllOverridingMethodsSearch
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.util.QueryExecutor
|
||||
import org.jetbrains.uast.UClass
|
||||
import org.jetbrains.uast.UMethod
|
||||
import org.jetbrains.uast.getContainingUClass
|
||||
import org.jetbrains.uast.toUElementOfType
|
||||
|
||||
internal class RemoteMethodImplementationSearcher : QueryExecutor<PsiMethod, OverridingMethodsSearch.SearchParameters> {
|
||||
override fun execute(queryParameters: OverridingMethodsSearch.SearchParameters, consumer: Processor<in PsiMethod>): Boolean {
|
||||
return ReadAction.compute<Boolean, Throwable> {
|
||||
if (queryParameters.method.hasModifier(JvmModifier.ABSTRACT)) {
|
||||
if (isRemoteInterface(queryParameters.method.containingClass)) {
|
||||
val remoteMethods = findRemoteMethods(queryParameters.method)
|
||||
for (m in remoteMethods) {
|
||||
if (!consumer.process(m)) {
|
||||
return@compute false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return@compute true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class RemoteMethodAllImplementationSearcher : QueryExecutor<Couple<PsiMethod>, AllOverridingMethodsSearch.SearchParameters> {
|
||||
override fun execute(queryParameters: AllOverridingMethodsSearch.SearchParameters, consumer: Processor<in Couple<PsiMethod>>): Boolean {
|
||||
return ReadAction.compute<Boolean, Throwable> {
|
||||
if (isRemoteInterface(queryParameters.psiClass)) {
|
||||
for (interfaceMethod in queryParameters.psiClass.allMethods) {
|
||||
val remoteMethods = findRemoteMethods(interfaceMethod)
|
||||
for (m in remoteMethods) {
|
||||
if (!consumer.process(Couple(interfaceMethod, m))) {
|
||||
return@compute false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return@compute true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun findRemoteMethods(psiMethod: PsiMethod): Collection<PsiMethod> {
|
||||
if (!psiMethod.isValid) return emptyList()
|
||||
|
||||
val uClass = psiMethod.toUElementOfType<UMethod>()?.getContainingUClass() ?: return emptyList()
|
||||
val remoteClass = getTargetRemoteClass(psiMethod.project, uClass) ?: return emptyList()
|
||||
|
||||
return remoteClass.allMethods
|
||||
.filter { it.name == psiMethod.name }
|
||||
.filterNot {
|
||||
it.hasModifier(JvmModifier.PRIVATE)
|
||||
|| it.hasModifier(JvmModifier.PROTECTED)
|
||||
|| it.hasModifier(JvmModifier.PACKAGE_LOCAL)
|
||||
}
|
||||
.filter { it.parameters.size == psiMethod.parameters.size }
|
||||
}
|
||||
|
||||
internal class RemoteInterfaceDirectImplementationSearcher : QueryExecutor<PsiClass, DirectClassInheritorsSearch.SearchParameters> {
|
||||
override fun execute(queryParameters: DirectClassInheritorsSearch.SearchParameters, consumer: Processor<in PsiClass>): Boolean {
|
||||
return processRemoteTargetClass(queryParameters.classToProcess, consumer)
|
||||
}
|
||||
}
|
||||
|
||||
internal class RemoteInterfaceImplementationSearcher : QueryExecutor<PsiClass, ClassInheritorsSearch.SearchParameters> {
|
||||
override fun execute(queryParameters: ClassInheritorsSearch.SearchParameters, consumer: Processor<in PsiClass>): Boolean {
|
||||
return processRemoteTargetClass(queryParameters.classToProcess, consumer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processRemoteTargetClass(classToProcess: PsiClass,
|
||||
consumer: Processor<in PsiClass>): Boolean {
|
||||
return ReadAction.compute<Boolean, Throwable> {
|
||||
if (isRemoteInterface(classToProcess)) {
|
||||
val uClass = classToProcess.toUElementOfType<UClass>() ?: return@compute true
|
||||
val targetClass = getTargetRemoteClass(classToProcess.project, uClass)
|
||||
if (targetClass != null) {
|
||||
if (!consumer.process(targetClass)) {
|
||||
return@compute false
|
||||
}
|
||||
}
|
||||
}
|
||||
return@compute true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package org.jetbrains.idea.devkit.driver
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil
|
||||
import com.intellij.lang.jvm.JvmModifier
|
||||
import com.intellij.openapi.application.QueryExecutorBase
|
||||
import com.intellij.openapi.application.ReadAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.FileIndexFacade
|
||||
import com.intellij.openapi.roots.TestSourcesFilter
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.GlobalSearchScope.EMPTY_SCOPE
|
||||
import com.intellij.psi.search.GlobalSearchScope.allScope
|
||||
import com.intellij.psi.search.RequestResultProcessor
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.UsageSearchContext
|
||||
import com.intellij.psi.search.searches.MethodReferencesSearch
|
||||
import com.intellij.psi.util.InheritanceUtil.isInheritorOrSelf
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.uast.*
|
||||
|
||||
internal class RemoteMethodReferenceSearcher :
|
||||
QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters>() {
|
||||
override fun processQuery(
|
||||
queryParameters: MethodReferencesSearch.SearchParameters,
|
||||
consumer: Processor<in PsiReference>
|
||||
) {
|
||||
val targetMethod = queryParameters.method
|
||||
val methodName = ReadAction.compute<String, Throwable> { targetMethod.name }
|
||||
|
||||
queryParameters.optimizer.searchWord(
|
||||
methodName,
|
||||
ReadAction.compute<SearchScope, RuntimeException> {
|
||||
if (targetMethod.hasModifier(JvmModifier.PRIVATE)
|
||||
|| targetMethod.hasModifier(JvmModifier.PACKAGE_LOCAL)
|
||||
|| targetMethod.hasModifier(JvmModifier.PROTECTED)
|
||||
) {
|
||||
return@compute EMPTY_SCOPE
|
||||
}
|
||||
|
||||
// do not search for self
|
||||
val searchableClass = targetMethod.containingClass ?: return@compute EMPTY_SCOPE
|
||||
if (isRemoteInterface(searchableClass)) return@compute EMPTY_SCOPE
|
||||
|
||||
val project = targetMethod.project
|
||||
val file = searchableClass.containingFile?.virtualFile ?: return@compute EMPTY_SCOPE
|
||||
if (TestSourcesFilter.isTestSources(file, project)
|
||||
|| FileIndexFacade.getInstance(project).isInLibrary(file)
|
||||
) {
|
||||
return@compute EMPTY_SCOPE
|
||||
}
|
||||
|
||||
val remoteClass = JavaPsiFacade.getInstance(project)
|
||||
.findClass(REMOTE_ANNOTATION_FQN, allScope(project))
|
||||
?: return@compute EMPTY_SCOPE
|
||||
|
||||
// we don't care about resolve scope of the method itself
|
||||
remoteClass.useScope.intersectWith(queryParameters.scopeDeterminedByUser)
|
||||
},
|
||||
UsageSearchContext.IN_CODE,
|
||||
true,
|
||||
targetMethod,
|
||||
object : RequestResultProcessor() {
|
||||
override fun processTextOccurrence(
|
||||
element: PsiElement,
|
||||
offsetInElement: Int,
|
||||
consumer: Processor<in PsiReference>
|
||||
): Boolean {
|
||||
val method = element.toUElement(UMethod::class.java) ?: return true
|
||||
|
||||
val uClass = method.getContainingUClass() ?: return true
|
||||
val psiMethodFound = method.javaPsi
|
||||
|
||||
if (psiMethodFound.parameters.size != targetMethod.parameters.size) {
|
||||
// parameter count mismatch
|
||||
return true
|
||||
}
|
||||
|
||||
val psiClass = psiMethodFound.containingClass ?: return true
|
||||
if (!isRemoteInterface(psiClass)) return true
|
||||
|
||||
val baseClass = getTargetRemoteClass(element.project, uClass) ?: return true
|
||||
|
||||
val searchableClass = targetMethod.containingClass
|
||||
if (!isInheritorOrSelf(searchableClass, baseClass, true)
|
||||
&& !isInheritorOrSelf(baseClass, searchableClass, true)) {
|
||||
return true
|
||||
}
|
||||
|
||||
val reference = PsiReferenceBase.createSelfReference(
|
||||
element,
|
||||
TextRange(offsetInElement, offsetInElement + methodName.length),
|
||||
targetMethod
|
||||
)
|
||||
|
||||
return consumer.process(reference)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun isRemoteInterface(clazz: PsiClass?): Boolean {
|
||||
return clazz != null && clazz.isInterface && AnnotationUtil.isAnnotated(clazz, REMOTE_ANNOTATION_FQN, 0)
|
||||
}
|
||||
|
||||
internal fun getTargetRemoteClass(project: Project, uClass: UClass): PsiClass? {
|
||||
val targetClassFqn = uClass.uAnnotations
|
||||
.find { it.qualifiedName == REMOTE_ANNOTATION_FQN }
|
||||
?.findAttributeValue("value")
|
||||
?.evaluateString()
|
||||
?: return null
|
||||
|
||||
return JavaPsiFacade.getInstance(project)
|
||||
.findClass(targetClassFqn, allScope(project))
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.jetbrains.idea.devkit.driver
|
||||
|
||||
import com.intellij.java.library.JavaLibraryUtil
|
||||
import com.intellij.lang.jvm.JvmModifier
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.search.GlobalSearchScope.allScope
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.UseScopeEnlarger
|
||||
|
||||
internal const val REMOTE_ANNOTATION_FQN = "com.intellij.driver.client.Remote"
|
||||
|
||||
internal class RemoteUseScopeEnlarger : UseScopeEnlarger() {
|
||||
override fun getAdditionalUseScope(element: PsiElement): SearchScope? {
|
||||
if (element !is PsiClass && element !is PsiMethod) return null
|
||||
|
||||
if (element is PsiMethod
|
||||
&& (element.hasModifier(JvmModifier.PRIVATE)
|
||||
|| element.hasModifier(JvmModifier.PACKAGE_LOCAL)
|
||||
|| element.hasModifier(JvmModifier.PROTECTED))
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
val project = element.project
|
||||
if (!JavaLibraryUtil.hasLibraryClass(project, REMOTE_ANNOTATION_FQN)) return null
|
||||
|
||||
val remoteClass = JavaPsiFacade.getInstance(project)
|
||||
.findClass(REMOTE_ANNOTATION_FQN, allScope(project)) ?: return null
|
||||
|
||||
return remoteClass.useScope
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user