Fix Kotlin definitions search for classes which can only be used in a local search scope (KT-13753)

This commit is contained in:
Dmitry Jemerov
2016-10-28 13:03:24 +02:00
parent e93f6e97cb
commit 127db98bbc
3 changed files with 93 additions and 5 deletions
@@ -17,9 +17,11 @@
package org.jetbrains.kotlin.idea.search.ideaExtensions
import com.intellij.codeInsight.navigation.MethodImplementationsSearch
import com.intellij.openapi.application.runReadAction
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.psi.search.searches.DefinitionsScopedSearch
@@ -27,9 +29,13 @@ import com.intellij.util.Processor
import com.intellij.util.QueryExecutor
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.contains
import java.util.*
class KotlinDefinitionsSearcher : QueryExecutor<PsiElement, DefinitionsScopedSearch.SearchParameters> {
@@ -42,6 +48,14 @@ class KotlinDefinitionsSearcher : QueryExecutor<PsiElement, DefinitionsScopedSea
is KtClass ->
processClassImplementations(element, consumer)
is KtLightClass -> {
val useScope = runReadAction { element.useScope }
if (useScope is LocalSearchScope)
processLightClassLocalImplementations(element, useScope, consumer)
else
true
}
is KtNamedFunction, is KtSecondaryConstructor ->
processFunctionImplementations(element as KtFunction, scope, consumer)
@@ -73,11 +87,30 @@ class KotlinDefinitionsSearcher : QueryExecutor<PsiElement, DefinitionsScopedSea
}
private fun processClassImplementations(klass: KtClass, consumer: Processor<PsiElement>): Boolean {
val psiClass = runReadAction { klass.toLightClass() }
if (psiClass != null) {
return ContainerUtil.process(ClassInheritorsSearch.search(psiClass, true), consumer)
val psiClass = runReadAction { klass.toLightClass() } ?: return true
val searchScope = runReadAction { psiClass.useScope }
if (searchScope is LocalSearchScope) {
return processLightClassLocalImplementations(psiClass, searchScope, consumer)
}
return true
return ContainerUtil.process(ClassInheritorsSearch.search(psiClass, true), consumer)
}
private fun processLightClassLocalImplementations(psiClass: KtLightClass,
searchScope: LocalSearchScope,
consumer: Processor<PsiElement>): Boolean {
// workaround for IDEA optimization that uses Java PSI traversal to locate inheritors in local search scope
val globalScope = GlobalSearchScope.filesScope(psiClass.project, searchScope.virtualFiles.toList())
return ContainerUtil.process(ClassInheritorsSearch.search(psiClass, globalScope, true), Processor<PsiClass> { candidate ->
val candidateOrigin = candidate.unwrapped ?: candidate
if (candidateOrigin in searchScope) {
consumer.process(candidate)
}
else {
true
}
})
}
private fun processFunctionImplementations(function: KtFunction, scope: SearchScope, consumer: Processor<PsiElement>): Boolean {
+8
View File
@@ -0,0 +1,8 @@
// CLASS: Outer.Base
// SEARCH: class:Derived
class Outer {
private abstract class Base
private class Derived : Base()
}
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.search
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.search.searches.DefinitionsScopedSearch
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File
class DefinitionsSearchTest : AbstractSearcherTest() {
fun testNestedClass() {
doTest()
}
private fun doTest() {
myFixture.configureByFile(fileName)
val directives = InTextDirectivesUtils.findListWithPrefixes(FileUtil.loadFile(File(pathToFile), true), "// CLASS: ")
assertFalse("Specify CLASS directive in test file", directives.isEmpty())
val superClassName = directives[0]
val psiClass = getPsiClass(superClassName)
checkResult(DefinitionsScopedSearch.search(psiClass))
val origin = (psiClass as? KtLightClass)?.kotlinOrigin!!
checkResult(DefinitionsScopedSearch.search(origin))
}
override fun getTestDataPath(): String {
return File(PluginTestCaseBase.getTestDataPathBase(), "/search/definitions").path + File.separator
}
}