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( UsagesSearchRequestItem requestItem = new UsagesSearchRequestItem(
searchTarget, searchTarget,
UsagesSearchPackage.getSpecialNamesToSearch(unwrappedElement), UsagesSearchPackage.getSpecialNamesToSearch(unwrappedElement),
UsagesSearchPackage.getIsTargetUsage() UsagesSearchPackage.getIsTargetUsage(),
null
); );
searchHelper.processFilesWithText(requestItem, consumer); searchHelper.processFilesWithText(requestItem, consumer);
} }
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchFilter.*
import java.util.Collections import java.util.Collections
import java.util.ArrayList import java.util.ArrayList
import com.intellij.psi.PsiNamedElement import com.intellij.psi.PsiNamedElement
import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.LightClassUtil.PropertyAccessorsPsiMethods import org.jetbrains.kotlin.asJava.LightClassUtil.PropertyAccessorsPsiMethods
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.*
@@ -33,7 +34,10 @@ import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.idea.references.* import org.jetbrains.kotlin.idea.references.*
import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.idea.caches.resolve.analyze 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.psi.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassReceiver
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
val isTargetUsage = (PsiReference::matchesTarget).searchFilter val isTargetUsage = (PsiReference::matchesTarget).searchFilter
@@ -113,8 +117,10 @@ public abstract class UsagesSearchHelper<T : PsiNamedElement> {
protected open fun makeItemList(target: UsagesSearchTarget<T>): List<UsagesSearchRequestItem> = protected open fun makeItemList(target: UsagesSearchTarget<T>): List<UsagesSearchRequestItem> =
Collections.singletonList(newItem(target)) Collections.singletonList(newItem(target))
protected open fun makeAdditionalSearchDelegate(target: T): ((Processor<PsiReference>) -> Boolean)? = null
fun newItem(target: UsagesSearchTarget<T>): UsagesSearchRequestItem { 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 { fun newRequest(target: UsagesSearchTarget<T>): UsagesSearchRequest {
@@ -151,6 +157,32 @@ class ClassUsagesSearchHelper(
!constructorUsages && !nonConstructorUsages -> False !constructorUsages && !nonConstructorUsages -> False
else -> True 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( class ClassDeclarationsUsagesSearchHelper(
@@ -103,7 +103,8 @@ public trait UsagesSearchFilter {
public data class UsagesSearchRequestItem( public data class UsagesSearchRequestItem(
val target: UsagesSearchTarget<PsiElement>, val target: UsagesSearchTarget<PsiElement>,
val words: List<String>, 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>) { 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>() { val executorImpl = object : QueryExecutorBase<PsiReference, UsagesSearchRequest>() {
override fun processQuery(request: UsagesSearchRequest, consumer: Processor<PsiReference>) { override fun processQuery(request: UsagesSearchRequest, consumer: Processor<PsiReference>) {
val searchHelper = KotlinPsiSearchHelper(request.project) 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); 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") @TestMetadata("javaUsage.0.kt")
public void testJavaUsage() throws Exception { public void testJavaUsage() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/defaultObject/javaUsage.0.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/defaultObject/javaUsage.0.kt");