Finding implicit usages of companion object in containing class.

This commit is contained in:
Evgeny Gerashchenko
2015-03-18 22:14:01 +03:00
parent 64937f0ccf
commit efccde2c0b
6 changed files with 75 additions and 4 deletions
@@ -84,7 +84,8 @@ public class KotlinReferencesSearcher extends QueryExecutorBase<PsiReference, Re
UsagesSearchRequestItem requestItem = new UsagesSearchRequestItem(
searchTarget,
UsagesSearchPackage.getSpecialNamesToSearch(unwrappedElement),
UsagesSearchPackage.getIsTargetUsage()
UsagesSearchPackage.getIsTargetUsage(),
null
);
searchHelper.processFilesWithText(requestItem, consumer);
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.*
import java.util.Collections
import java.util.ArrayList
import com.intellij.psi.PsiNamedElement
import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.LightClassUtil.PropertyAccessorsPsiMethods
import org.jetbrains.kotlin.psi.psiUtil.*
@@ -33,7 +34,10 @@ import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.idea.references.*
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassReceiver
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
val isTargetUsage = (PsiReference::matchesTarget).searchFilter
@@ -113,8 +117,10 @@ public abstract class UsagesSearchHelper<T : PsiNamedElement> {
protected open fun makeItemList(target: UsagesSearchTarget<T>): List<UsagesSearchRequestItem> =
Collections.singletonList(newItem(target))
protected open fun makeAdditionalSearchDelegate(target: T): ((Processor<PsiReference>) -> Boolean)? = null
fun newItem(target: UsagesSearchTarget<T>): UsagesSearchRequestItem {
return UsagesSearchRequestItem(target, makeWordList(target), makeFilter(target))
return UsagesSearchRequestItem(target, makeWordList(target), makeFilter(target), makeAdditionalSearchDelegate(target.element))
}
fun newRequest(target: UsagesSearchTarget<T>): UsagesSearchRequest {
@@ -151,6 +157,32 @@ class ClassUsagesSearchHelper(
!constructorUsages && !nonConstructorUsages -> False
else -> True
}
override fun makeAdditionalSearchDelegate(target: JetClassOrObject): ((Processor<PsiReference>) -> Boolean)? {
if (target is JetObjectDeclaration && target.isCompanion()) {
val companionObjectDescriptor = target.descriptor
val klass = target.getStrictParentOfType<JetClass>() ?: return null
return { processor ->
klass.acceptChildren(object : JetVisitorVoid() {
override fun visitJetElement(element: JetElement) {
if (element == target) return // skip companion object itself
element.acceptChildren(this)
val bindingContext = element.analyze()
val call = bindingContext[BindingContext.CALL, element] ?: return
val resolvedCall = bindingContext[BindingContext.RESOLVED_CALL, call] ?: return
if ((resolvedCall.getDispatchReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor
|| (resolvedCall.getExtensionReceiver() as? ClassReceiver)?.getDeclarationDescriptor() == companionObjectDescriptor) {
element.getReference()?.let { processor.process(it) }
}
}
})
true
}
}
return null
}
}
class ClassDeclarationsUsagesSearchHelper(
@@ -103,7 +103,8 @@ public trait UsagesSearchFilter {
public data class UsagesSearchRequestItem(
val target: UsagesSearchTarget<PsiElement>,
val words: List<String>,
val filter: UsagesSearchFilter
val filter: UsagesSearchFilter,
val additionalSearchDelegate: ((Processor<PsiReference>) -> Boolean)?
)
public data class UsagesSearchRequest(val project: Project, val items: List<UsagesSearchRequestItem>) {
@@ -164,7 +165,8 @@ public object UsagesSearch: QueryFactory<PsiReference, UsagesSearchRequest>() {
val executorImpl = object : QueryExecutorBase<PsiReference, UsagesSearchRequest>() {
override fun processQuery(request: UsagesSearchRequest, consumer: Processor<PsiReference>) {
val searchHelper = KotlinPsiSearchHelper(request.project)
request.items.filter { it.filter != False }.all { searchHelper.processFilesWithText(it, consumer) }
request.items.filter { it.filter != False }.all {
searchHelper.processFilesWithText(it, consumer) && (it.additionalSearchDelegate?.invoke(consumer) ?: true) }
}
}
@@ -0,0 +1,26 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.JetObjectDeclaration
// OPTIONS: usages
class A {
init {
foo()
v
1[2] // using companion object function by convention
ext() // companion object is extension receiver
}
companion <caret>object: Foo {
fun foo() {
}
val v = 42
fun Int.get(a: Int) = this + a
}
}
trait Foo
fun Foo.ext() {
}
@@ -0,0 +1,4 @@
Function call (10: 9) ext() // companion object is extension receiver
Function call (5: 9) foo()
Implicit 'get' (8: 9) 1[2] // using companion object function by convention
Value read (6: 9) v
@@ -176,6 +176,12 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/findUsages/kotlin/defaultObject"), Pattern.compile("^(.+)\\.0\\.kt$"), true);
}
@TestMetadata("inContainingClass.0.kt")
public void testInContainingClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/defaultObject/inContainingClass.0.kt");
doTest(fileName);
}
@TestMetadata("javaUsage.0.kt")
public void testJavaUsage() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/defaultObject/javaUsage.0.kt");