Implement search helpers for Kotlin declarations

This commit is contained in:
Alexey Sedunov
2013-10-24 19:24:39 +04:00
parent 790fb2acff
commit e1fa8628ec
4 changed files with 329 additions and 71 deletions
@@ -1,69 +0,0 @@
/*
* Copyright 2010-2013 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.jet.plugin.findUsages
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiConstructorCall
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.psi.*
import org.jetbrains.jet.lang.psi.psiUtil.*
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.resolve.BindingContextUtils
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
fun PsiElement.isConstructorUsage(jetClassOrObject: JetClassOrObject): Boolean {
fun getCallDescriptor(bindingContext: BindingContext): DeclarationDescriptor? {
val constructorCalleeExpression = getParentByType(javaClass<JetConstructorCalleeExpression>())
if (constructorCalleeExpression != null) {
return bindingContext.get(BindingContext.REFERENCE_TARGET, constructorCalleeExpression.getConstructorReferenceExpression())
}
val callExpression = getParentByType(javaClass<JetCallExpression>())
if (callExpression != null) {
val callee = callExpression.getCalleeExpression()
if (callee is JetReferenceExpression) {
return bindingContext.get(BindingContext.REFERENCE_TARGET, callee)
}
}
return null
}
fun checkJavaUsage(): Boolean {
val call = getParentByType(javaClass<PsiConstructorCall>())
return call != null && call == getParent()
&& call.resolveConstructor()?.getContainingClass()?.getNavigationElement() == jetClassOrObject
}
fun checkKotlinUsage(): Boolean {
val file = getContainingFile()
if (file !is JetFile) return false
val bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache(file).getBindingContext()
val descriptor = getCallDescriptor(bindingContext)
if (descriptor !is ConstructorDescriptor) return false
return BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor.getContainingDeclaration()) == jetClassOrObject
}
return checkJavaUsage() || checkKotlinUsage()
}
@@ -35,10 +35,10 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.plugin.findUsages.FindUsagesPackage;
import org.jetbrains.jet.plugin.hierarchy.HierarchyUtils;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.plugin.references.JetPsiReference;
import org.jetbrains.jet.plugin.search.usagesSearch.UsagesSearchPackage;
import java.util.ArrayList;
import java.util.Map;
@@ -150,7 +150,7 @@ public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStr
new Condition<PsiReference>() {
@Override
public boolean value(PsiReference reference) {
return FindUsagesPackage.isConstructorUsage(reference.getElement(), classOrObject);
return UsagesSearchPackage.isConstructorUsage(reference, classOrObject);
}
},
defaultQueryProcessor(descriptor, methodToDescriptorMap, false)
@@ -0,0 +1,172 @@
/*
* Copyright 2010-2013 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.jet.plugin.search.usagesSearch
import com.intellij.psi.PsiReference
import org.jetbrains.jet.lang.psi.JetClassOrObject
import org.jetbrains.jet.lang.psi.psiUtil.getParentByType
import org.jetbrains.jet.lang.psi.JetImportDirective
import org.jetbrains.jet.plugin.search.usagesSearch.*
import org.jetbrains.jet.plugin.search.usagesSearch.UsagesSearchFilter.*
import org.jetbrains.jet.lang.psi.JetProperty
import org.jetbrains.jet.lang.psi.JetNamedFunction
import java.util.Collections
import java.util.ArrayList
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
import com.intellij.psi.PsiNamedElement
import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.jet.asJava.LightClassUtil
import com.intellij.psi.PsiMethod
import com.intellij.util.Query
import com.intellij.psi.search.SearchRequestCollector
import org.jetbrains.jet.lang.psi.JetCallableDeclaration
val isTargetUsage = (PsiReference::isTargetUsage).searchFilter
fun JetProperty.names(readable: Boolean = true, writable: Boolean = true): List<String> {
val name = getName()!!
if (isLocal()) return Collections.singletonList(name)
val lightMethods = LightClassUtil.getLightClassPropertyMethods(this)
val getter = lightMethods.getGetter()
val setter = lightMethods.getSetter()
val result = arrayListOf(name)
if (readable && getter != null) result.add(getter.getName())
if (writable && setter != null) result.add(setter.getName())
return result
}
public abstract class UsagesSearchHelper<T : PsiNamedElement> {
protected open fun makeFilter(target: UsagesSearchTarget<T>): UsagesSearchFilter = isTargetUsage
protected open fun makeWordList(target: UsagesSearchTarget<T>): List<String> {
return with(target) {
val name = element.getName()
when {
name == null -> Collections.emptyList<String>()
element is JetProperty -> element.names()
else -> Collections.singletonList(name)
}
}
}
protected open fun makeItemList(target: UsagesSearchTarget<T>): List<UsagesSearchRequestItem> =
Collections.singletonList(newItem(target))
fun newItem(target: UsagesSearchTarget<T>): UsagesSearchRequestItem {
return UsagesSearchRequestItem(target, makeWordList(target), makeFilter(target))
}
fun newRequest(target: UsagesSearchTarget<T>): UsagesSearchRequest {
return UsagesSearchRequest(target.element.getProject(), makeItemList(target))
}
}
object DefaultUsagesSearchHelper: UsagesSearchHelper<PsiNamedElement>()
val isNotImportUsage = !((PsiReference::isImportUsage).searchFilter)
open class DeclarationUsagesSearchHelper<T : PsiNamedElement> : UsagesSearchHelper<T>() {
public var skipImports: Boolean = false
protected val isFilteredImport: UsagesSearchFilter
get() = isNotImportUsage.ifOrTrue(skipImports)
override fun makeFilter(target: UsagesSearchTarget<T>): UsagesSearchFilter = isTargetUsage and isFilteredImport
}
val isClassConstructorUsage = (PsiReference::isConstructorUsage).searchFilter
class ClassUsagesSearchHelper : DeclarationUsagesSearchHelper<JetClassOrObject>() {
public var constructorUsages: Boolean = false
public var nonConstructorUsages: Boolean = false
override fun makeFilter(target: UsagesSearchTarget<JetClassOrObject>): UsagesSearchFilter =
super.makeFilter(target) and when {
constructorUsages && !nonConstructorUsages -> isClassConstructorUsage
!constructorUsages && nonConstructorUsages -> !isClassConstructorUsage
!constructorUsages && !nonConstructorUsages -> False
else -> True
}
}
class ClassDeclarationsUsagesSearchHelper : DeclarationUsagesSearchHelper<JetClassOrObject>() {
public var functionUsages: Boolean = false
public var propertyUsages: Boolean = false
override fun makeItemList(target: UsagesSearchTarget<JetClassOrObject>): List<UsagesSearchRequestItem> {
val items = ArrayList<UsagesSearchRequestItem>()
val declHelper = DeclarationUsagesSearchHelper<JetNamedDeclaration>(skipImports)
for (decl in target.element.getDeclarations()) {
if ((decl is JetNamedFunction && functionUsages) || (decl is JetProperty && propertyUsages)) {
items.add(declHelper.newItem(target.retarget(decl as JetNamedDeclaration)))
}
}
return items
}
}
val isOverrideUsage = (PsiReference::isCallableOverrideUsage).searchFilter
abstract class CallableUsagesSearchHelper<T: JetCallableDeclaration>: DeclarationUsagesSearchHelper<T>() {
public var selfUsages: Boolean = true
public var overrideUsages: Boolean = true
val isTargetOrOverrideUsage: UsagesSearchFilter
get() = isTargetUsage.ifOrFalse(selfUsages) or isOverrideUsage.ifOrFalse(overrideUsages)
}
val isOverloadUsage = (PsiReference::isUsageInContainingDeclaration).searchFilter
val isExtensionUsage = (PsiReference::isExtensionOfDeclarationClassUsage).searchFilter
class FunctionUsagesSearchHelper() : CallableUsagesSearchHelper<JetNamedFunction>() {
public var overloadUsages: Boolean = false
public var extensionUsages: Boolean = false
override fun makeFilter(target: UsagesSearchTarget<JetNamedFunction>): UsagesSearchFilter {
return (isTargetOrOverrideUsage
or isOverloadUsage.ifOrFalse(overloadUsages)
or isExtensionUsage.ifOrFalse(extensionUsages)) and isFilteredImport
}
}
val isPropertyReadOnlyUsage = (PsiReference::isPropertyReadOnlyUsage).searchFilter
class PropertyUsagesSearchHelper() : CallableUsagesSearchHelper<JetProperty>() {
public var readUsages: Boolean = true
public var writeUsages: Boolean = true
override fun makeWordList(target: UsagesSearchTarget<JetProperty>): List<String> {
return target.element.names(readable = readUsages, writable = writeUsages)
}
override fun makeFilter(target: UsagesSearchTarget<JetProperty>): UsagesSearchFilter {
val readWriteUsage = when {
readUsages && writeUsages -> True
readUsages -> isPropertyReadOnlyUsage
writeUsages -> !isPropertyReadOnlyUsage
else -> False
}
return isTargetOrOverrideUsage and readWriteUsage and isFilteredImport
}
}
@@ -0,0 +1,155 @@
/*
* Copyright 2010-2013 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.jet.plugin.search.usagesSearch
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiConstructorCall
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.psi.*
import org.jetbrains.jet.lang.psi.psiUtil.*
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.resolve.BindingContextUtils
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import com.intellij.psi.PsiReference
import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetClsMethod
import org.jetbrains.jet.lang.resolve.OverridingUtil
import org.jetbrains.jet.plugin.references.JetPsiReference
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
import org.jetbrains.jet.plugin.findUsages.JetUsageTypeProvider
import org.jetbrains.jet.plugin.findUsages.JetUsageTypes
import com.intellij.usages.impl.rules.UsageType
import org.jetbrains.jet.asJava.LightClassUtil
import org.jetbrains.jet.lang.resolve.java.JvmAbi
import org.jetbrains.jet.codegen.PropertyCodegen
val PsiReference.navigationTarget: PsiElement?
get() {
val target = resolve()?.getNavigationElement()
return if (target is JetPropertyAccessor) target.getParentByType(javaClass<JetProperty>()) else target
}
val JetDeclaration.descriptor: DeclarationDescriptor?
get() = AnalyzerFacadeWithCache.getContextForElement(this).get(BindingContext.DECLARATION_TO_DESCRIPTOR, this)
fun PsiReference.isTargetUsage(target: PsiElement): Boolean {
return target.getNavigationElement() == navigationTarget
}
fun PsiReference.checkUsageVsOriginalDescriptor(
target: JetDeclaration, checker: (usageDescriptor: DeclarationDescriptor, targetDescriptor: DeclarationDescriptor) -> Boolean
): Boolean {
val refTarget = navigationTarget
if (refTarget !is JetDeclaration) return false
val usageDescriptor = refTarget.descriptor
val targetDescriptor = target.descriptor
return usageDescriptor != null && targetDescriptor != null && checker(usageDescriptor, targetDescriptor)
}
fun PsiReference.isImportUsage(): Boolean =
getElement()!!.getParentByType(javaClass<JetImportDirective>()) != null
fun PsiReference.isConstructorUsage(jetClassOrObject: JetClassOrObject): Boolean = with (getElement()!!) {
fun getCallDescriptor(bindingContext: BindingContext): DeclarationDescriptor? {
val constructorCalleeExpression = getParentByType(javaClass<JetConstructorCalleeExpression>())
if (constructorCalleeExpression != null) {
return bindingContext.get(BindingContext.REFERENCE_TARGET, constructorCalleeExpression.getConstructorReferenceExpression())
}
val callExpression = getParentByType(javaClass<JetCallExpression>())
if (callExpression != null) {
val callee = callExpression.getCalleeExpression()
if (callee is JetReferenceExpression) {
return bindingContext.get(BindingContext.REFERENCE_TARGET, callee)
}
}
return null
}
fun checkJavaUsage(): Boolean {
val call = getParentByType(javaClass<PsiConstructorCall>())
return call != null && call == getParent()
&& call.resolveConstructor()?.getContainingClass()?.getNavigationElement() == jetClassOrObject
}
fun checkKotlinUsage(): Boolean {
val file = getContainingFile()
if (file !is JetFile) return false
val bindingContext = AnalyzerFacadeWithCache.analyzeFileWithCache(file).getBindingContext()
val descriptor = getCallDescriptor(bindingContext)
if (descriptor !is ConstructorDescriptor) return false
return BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor.getContainingDeclaration()) == jetClassOrObject
}
checkJavaUsage() || checkKotlinUsage()
}
fun PsiReference.isExtensionOfDeclarationClassUsage(declaration: JetNamedDeclaration): Boolean =
checkUsageVsOriginalDescriptor(declaration) { (usageDescriptor, targetDescriptor) ->
when {
usageDescriptor == targetDescriptor -> false
usageDescriptor !is FunctionDescriptor -> false
else -> {
val receiverDescriptor =
usageDescriptor.getReceiverParameter()?.getType()?.getConstructor()?.getDeclarationDescriptor()
val containingDescriptor = targetDescriptor.getContainingDeclaration()
containingDescriptor == receiverDescriptor
|| (containingDescriptor is ClassDescriptor
&& receiverDescriptor is ClassDescriptor
&& DescriptorUtils.isSubclass(containingDescriptor, receiverDescriptor))
}
}
}
fun PsiReference.isUsageInContainingDeclaration(declaration: JetNamedDeclaration): Boolean =
checkUsageVsOriginalDescriptor(declaration) { (usageDescriptor, targetDescriptor) ->
usageDescriptor != targetDescriptor
&& usageDescriptor.getContainingDeclaration() == targetDescriptor.getContainingDeclaration()
}
fun PsiReference.isCallableOverrideUsage(declaration: JetCallableDeclaration): Boolean =
checkUsageVsOriginalDescriptor(declaration) { (usageDescriptor, targetDescriptor) ->
usageDescriptor is CallableDescriptor && targetDescriptor is CallableDescriptor
&& OverridingUtil.overrides(usageDescriptor, targetDescriptor)
}
fun PsiReference.isPropertyReadOnlyUsage(): Boolean {
if (this is JetPsiReference) {
return JetUsageTypeProvider.getUsageType(getElement()) != UsageType.WRITE
}
val refTarget = resolve()
if (refTarget is JetClsMethod) {
val property = refTarget.getOrigin()?.getParentByType(javaClass<JetProperty>())
return property != null && refTarget.getName() == PropertyCodegen.getterName(property.getNameAsName())
}
return false
}