Find Usages: Look for as-property usages of Java methods

This commit is contained in:
Alexey Sedunov
2015-10-21 12:44:59 +03:00
committed by Alexey Sedunov
parent 7e9222f70a
commit 9c360ef05a
34 changed files with 555 additions and 108 deletions
@@ -1047,7 +1047,7 @@ public class DescriptorResolver {
else if (property.isVar()) { else if (property.isVar()) {
Annotations setterAnnotations = annotationSplitter.getAnnotationsForTarget(PROPERTY_SETTER); Annotations setterAnnotations = annotationSplitter.getAnnotationsForTarget(PROPERTY_SETTER);
setterDescriptor = DescriptorFactory.createSetter(propertyDescriptor, setterAnnotations, !property.hasDelegate(), setterDescriptor = DescriptorFactory.createSetter(propertyDescriptor, setterAnnotations, !property.hasDelegate(),
/* isExternal = */ false); /* isExternal = */ false, propertyDescriptor.getSource());
} }
if (!property.isVar()) { if (!property.isVar()) {
@@ -76,7 +76,7 @@ public class JavaPropertyDescriptor extends PropertyDescriptorImpl implements Ja
if (getter != null) { if (getter != null) {
newGetter = new PropertyGetterDescriptorImpl( newGetter = new PropertyGetterDescriptorImpl(
enhanced, getter.getAnnotations(), getter.getModality(), getter.getVisibility(), enhanced, getter.getAnnotations(), getter.getModality(), getter.getVisibility(),
getter.hasBody(), getter.isDefault(), getter.isExternal(), getKind(), getter, SourceElement.NO_SOURCE); getter.hasBody(), getter.isDefault(), getter.isExternal(), getKind(), getter, getter.getSource());
newGetter.initialize(enhancedReturnType); newGetter.initialize(enhancedReturnType);
} }
@@ -85,7 +85,7 @@ public class JavaPropertyDescriptor extends PropertyDescriptorImpl implements Ja
if (setter != null) { if (setter != null) {
newSetter = new PropertySetterDescriptorImpl( newSetter = new PropertySetterDescriptorImpl(
enhanced, setter.getAnnotations(), setter.getModality(), setter.getVisibility(), enhanced, setter.getAnnotations(), setter.getModality(), setter.getVisibility(),
setter.hasBody(), setter.isDefault(), setter.isExternal(), getKind(), setter, SourceElement.NO_SOURCE); setter.hasBody(), setter.isDefault(), setter.isExternal(), getKind(), setter, setter.getSource());
newSetter.initialize(setter.getValueParameters().get(0)); newSetter.initialize(setter.getValueParameters().get(0));
} }
@@ -322,7 +322,7 @@ public class LazyJavaClassMemberScope(
val setter = setterMethod?.let { setterMethod -> val setter = setterMethod?.let { setterMethod ->
DescriptorFactory.createSetter(propertyDescriptor, setterMethod.annotations, /* isDefault = */false, DescriptorFactory.createSetter(propertyDescriptor, setterMethod.annotations, /* isDefault = */false,
/* isExternal = */ false, setterMethod.visibility) /* isExternal = */ false, setterMethod.visibility, setterMethod.source)
} }
return propertyDescriptor.apply { initialize(getter, setter) } return propertyDescriptor.apply { initialize(getter, setter) }
@@ -50,17 +50,7 @@ public class DescriptorFactory {
@NotNull PropertyDescriptor propertyDescriptor, @NotNull PropertyDescriptor propertyDescriptor,
@NotNull Annotations annotations @NotNull Annotations annotations
) { ) {
return createSetter(propertyDescriptor, annotations, true, false); return createSetter(propertyDescriptor, annotations, true, false, propertyDescriptor.getSource());
}
@NotNull
public static PropertySetterDescriptorImpl createSetter(
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull Annotations annotations,
boolean isDefault,
boolean isExternal
) {
return createSetter(propertyDescriptor, annotations, isDefault, isExternal, propertyDescriptor.getVisibility());
} }
@NotNull @NotNull
@@ -69,12 +59,24 @@ public class DescriptorFactory {
@NotNull Annotations annotations, @NotNull Annotations annotations,
boolean isDefault, boolean isDefault,
boolean isExternal, boolean isExternal,
@NotNull Visibility visibility @NotNull SourceElement sourceElement
) {
return createSetter(propertyDescriptor, annotations, isDefault, isExternal, propertyDescriptor.getVisibility(), sourceElement);
}
@NotNull
public static PropertySetterDescriptorImpl createSetter(
@NotNull PropertyDescriptor propertyDescriptor,
@NotNull Annotations annotations,
boolean isDefault,
boolean isExternal,
@NotNull Visibility visibility,
@NotNull SourceElement sourceElement
) { ) {
PropertySetterDescriptorImpl setterDescriptor = PropertySetterDescriptorImpl setterDescriptor =
new PropertySetterDescriptorImpl(propertyDescriptor, annotations, propertyDescriptor.getModality(), new PropertySetterDescriptorImpl(propertyDescriptor, annotations, propertyDescriptor.getModality(),
visibility, !isDefault, isDefault, isExternal, visibility, !isDefault, isDefault, isExternal,
CallableMemberDescriptor.Kind.DECLARATION, null, propertyDescriptor.getSource()); CallableMemberDescriptor.Kind.DECLARATION, null, sourceElement);
setterDescriptor.initializeDefault(); setterDescriptor.initializeDefault();
return setterDescriptor; return setterDescriptor;
} }
@@ -21,12 +21,15 @@ import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.IncorrectOperationException import com.intellij.util.IncorrectOperationException
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.util.ShortenReferences import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.lexer.KtToken import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.isOneSegmentFQN import org.jetbrains.kotlin.name.isOneSegmentFQN
@@ -36,10 +39,37 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleReference<KtSimpleNameExpression>(expression) { class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleReference<KtSimpleNameExpression>(expression) {
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
return SmartList<DeclarationDescriptor>().apply {
// Replace Java property with its accessor(s)
for (descriptor in super.getTargetDescriptors(context)) {
val sizeBefore = size
if (descriptor !is JavaPropertyDescriptor) {
add(descriptor)
continue
}
val readWriteAccess = expression.readWriteAccess(true)
descriptor.getter?.let {
if (readWriteAccess.isRead) add(it)
}
descriptor.setter?.let {
if (readWriteAccess.isWrite) add(it)
}
if (size == sizeBefore) {
add(descriptor)
}
}
}
}
override fun isReferenceTo(element: PsiElement?): Boolean { override fun isReferenceTo(element: PsiElement?): Boolean {
if (element != null) { if (element != null) {
if (!canBeReferenceTo(element)) return false if (!canBeReferenceTo(element)) return false
@@ -148,8 +148,8 @@ val KtElement.mainReference: KtReference?
// ----------- Read/write access ----------------------------------------------------------------------------------------------------------------------- // ----------- Read/write access -----------------------------------------------------------------------------------------------------------------------
public enum class ReferenceAccess { public enum class ReferenceAccess(val isRead: Boolean, val isWrite: Boolean) {
READ, WRITE, READ_WRITE READ(true, false), WRITE(false, true), READ_WRITE(true, true)
} }
public fun KtExpression.readWriteAccess(useResolveForReadWrite: Boolean): ReferenceAccess { public fun KtExpression.readWriteAccess(useResolveForReadWrite: Boolean): ReferenceAccess {
@@ -16,10 +16,13 @@
package org.jetbrains.kotlin.idea.search package org.jetbrains.kotlin.idea.search
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.SearchScope import com.intellij.psi.search.SearchScope
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.psi.KtFile
public fun SearchScope.and(otherScope: SearchScope): SearchScope = intersectWith(otherScope) public fun SearchScope.and(otherScope: SearchScope): SearchScope = intersectWith(otherScope)
public fun SearchScope.or(otherScope: SearchScope): SearchScope = union(otherScope) public fun SearchScope.or(otherScope: SearchScope): SearchScope = union(otherScope)
@@ -30,4 +33,19 @@ public fun Project.allScope(): GlobalSearchScope = GlobalSearchScope.allScope(th
public fun Project.projectScope(): GlobalSearchScope = GlobalSearchScope.projectScope(this) public fun Project.projectScope(): GlobalSearchScope = GlobalSearchScope.projectScope(this)
public fun PsiFile.fileScope(): GlobalSearchScope = GlobalSearchScope.fileScope(this) public fun PsiFile.fileScope(): GlobalSearchScope = GlobalSearchScope.fileScope(this)
public fun SearchScope.restrictToKotlinSources(): SearchScope {
return when (this) {
is GlobalSearchScope -> GlobalSearchScope.getScopeRestrictedByFileTypes(this, KotlinFileType.INSTANCE)
is LocalSearchScope -> {
val ktElements = scope.filter { it.containingFile is KtFile }
when (ktElements.size) {
0 -> GlobalSearchScope.EMPTY_SCOPE
scope.size -> this
else -> LocalSearchScope(ktElements.toTypedArray())
}
}
else -> this
}
}
@@ -21,8 +21,10 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference import com.intellij.psi.PsiReference
import com.intellij.psi.search.SearchScope import com.intellij.psi.search.SearchScope
import com.intellij.psi.util.MethodSignatureUtil
import org.jetbrains.kotlin.asJava.KotlinLightElement import org.jetbrains.kotlin.asJava.KotlinLightElement
import org.jetbrains.kotlin.asJava.KotlinNoOriginLightMethod import org.jetbrains.kotlin.asJava.KotlinNoOriginLightMethod
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -206,21 +208,28 @@ fun PsiReference.isUsageInContainingDeclaration(declaration: KtNamedDeclaration)
} }
fun PsiReference.isCallableOverrideUsage(declaration: KtNamedDeclaration): Boolean { fun PsiReference.isCallableOverrideUsage(declaration: KtNamedDeclaration): Boolean {
val toDescriptor: (KtDeclaration) -> DeclarationDescriptor? = { declaration -> val toDescriptor: (KtDeclaration) -> CallableDescriptor? = { declaration ->
if (declaration is KtParameter) { if (declaration is KtParameter) {
// we don't treat parameters in overriding method as "override" here (overriding parameters usages are searched optionally and via searching of overriding methods first) // we don't treat parameters in overriding method as "override" here (overriding parameters usages are searched optionally and via searching of overriding methods first)
if (declaration.hasValOrVar()) declaration.propertyDescriptor else null if (declaration.hasValOrVar()) declaration.propertyDescriptor else null
} }
else { else {
declaration.descriptor declaration.descriptor as? CallableDescriptor
} }
} }
val descriptor = toDescriptor(declaration) ?: return false val targetDescriptor = toDescriptor(declaration) ?: return false
return checkUsageVsOriginalDescriptor(descriptor, toDescriptor) { usageDescriptor, targetDescriptor -> return unwrappedTargets.any {
usageDescriptor is CallableDescriptor when (it) {
&& targetDescriptor is CallableDescriptor is KtDeclaration -> {
&& OverrideResolver.overrides(usageDescriptor, targetDescriptor) val usageDescriptor = toDescriptor(it)
usageDescriptor != null && OverrideResolver.overrides(usageDescriptor, targetDescriptor)
}
is PsiMethod -> {
declaration.toLightMethods().any { superMethod -> MethodSignatureUtil.isSuperMethod(superMethod, it) }
}
else -> false
}
} }
} }
@@ -25,6 +25,11 @@ public fun runReadAction<T>(action: () -> T): T {
return ApplicationManager.getApplication().runReadAction<T>(action) return ApplicationManager.getApplication().runReadAction<T>(action)
} }
public fun <T> Project.runReadActionInSmartMode(action: () -> T): T {
if (ApplicationManager.getApplication().isReadAccessAllowed) return action()
return DumbService.getInstance(this).runReadActionInSmartMode<T>(action)
}
public fun runWriteAction<T>(action: () -> T): T { public fun runWriteAction<T>(action: () -> T): T {
return ApplicationManager.getApplication().runWriteAction<T>(action) return ApplicationManager.getApplication().runWriteAction<T>(action)
} }
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.findUsages package org.jetbrains.kotlin.idea.findUsages
import com.intellij.find.findUsages.FindUsagesOptions
import com.intellij.find.findUsages.JavaClassFindUsagesOptions import com.intellij.find.findUsages.JavaClassFindUsagesOptions
import com.intellij.find.findUsages.JavaMethodFindUsagesOptions import com.intellij.find.findUsages.JavaMethodFindUsagesOptions
import com.intellij.find.findUsages.JavaVariableFindUsagesOptions import com.intellij.find.findUsages.JavaVariableFindUsagesOptions
@@ -35,6 +36,8 @@ public class KotlinClassFindUsagesOptions(project: Project) : JavaClassFindUsage
public interface KotlinCallableFindUsagesOptions { public interface KotlinCallableFindUsagesOptions {
public var searchOverrides: Boolean public var searchOverrides: Boolean
fun toJavaOptions(project: Project): FindUsagesOptions?
} }
public class KotlinFunctionFindUsagesOptions(project: Project): KotlinCallableFindUsagesOptions, JavaMethodFindUsagesOptions(project) { public class KotlinFunctionFindUsagesOptions(project: Project): KotlinCallableFindUsagesOptions, JavaMethodFindUsagesOptions(project) {
@@ -43,37 +46,36 @@ public class KotlinFunctionFindUsagesOptions(project: Project): KotlinCallableFi
set(value: Boolean) { set(value: Boolean) {
isOverridingMethods = value isOverridingMethods = value
} }
}
fun KotlinFunctionFindUsagesOptions.toJavaMethodOptions(project: Project): JavaMethodFindUsagesOptions { override fun toJavaOptions(project: Project): FindUsagesOptions? {
val javaOptions = JavaMethodFindUsagesOptions(project) val javaOptions = JavaMethodFindUsagesOptions(project)
javaOptions.fastTrack = fastTrack javaOptions.fastTrack = fastTrack
javaOptions.isCheckDeepInheritance = isCheckDeepInheritance javaOptions.isCheckDeepInheritance = isCheckDeepInheritance
javaOptions.isImplementingMethods = isImplementingMethods javaOptions.isImplementingMethods = isImplementingMethods
javaOptions.isIncludeInherited = isIncludeInherited javaOptions.isIncludeInherited = isIncludeInherited
javaOptions.isIncludeOverloadUsages = isIncludeOverloadUsages javaOptions.isIncludeOverloadUsages = isIncludeOverloadUsages
javaOptions.isOverridingMethods = isOverridingMethods javaOptions.isOverridingMethods = isOverridingMethods
javaOptions.isSearchForTextOccurrences = isSearchForTextOccurrences javaOptions.isSearchForTextOccurrences = isSearchForTextOccurrences
javaOptions.isSkipImportStatements = isSkipImportStatements javaOptions.isSkipImportStatements = isSkipImportStatements
javaOptions.isUsages = isUsages javaOptions.isUsages = isUsages
javaOptions.searchScope = searchScope javaOptions.searchScope = searchScope
return javaOptions
}
fun KotlinPropertyFindUsagesOptions.toJavaVariableOptions(project: Project): JavaVariableFindUsagesOptions { return javaOptions
val javaOptions = JavaVariableFindUsagesOptions(project) }
javaOptions.fastTrack = fastTrack
javaOptions.isSearchForTextOccurrences = isSearchForTextOccurrences
javaOptions.isSkipImportStatements = isSkipImportStatements
javaOptions.isReadAccess = isReadAccess
javaOptions.isWriteAccess = isWriteAccess
javaOptions.isUsages = isUsages
javaOptions.searchScope = searchScope
return javaOptions
} }
public class KotlinPropertyFindUsagesOptions(project: Project): KotlinCallableFindUsagesOptions, JavaVariableFindUsagesOptions(project) { public class KotlinPropertyFindUsagesOptions(project: Project): KotlinCallableFindUsagesOptions, JavaVariableFindUsagesOptions(project) {
override var searchOverrides: Boolean = false override var searchOverrides: Boolean = false
}
override fun toJavaOptions(project: Project): JavaVariableFindUsagesOptions {
val javaOptions = JavaVariableFindUsagesOptions(project)
javaOptions.fastTrack = fastTrack
javaOptions.isSearchForTextOccurrences = isSearchForTextOccurrences
javaOptions.isSkipImportStatements = isSkipImportStatements
javaOptions.isReadAccess = isReadAccess
javaOptions.isWriteAccess = isWriteAccess
javaOptions.isUsages = isUsages
javaOptions.searchScope = searchScope
return javaOptions
}
}
@@ -47,15 +47,9 @@ class DelegatingFindMemberUsagesHandler(
is KtNamedDeclaration -> is KtNamedDeclaration ->
HandlerAndOptions(KotlinFindMemberUsagesHandler.getInstance(element, elementsToSearch, factory), options) HandlerAndOptions(KotlinFindMemberUsagesHandler.getInstance(element, elementsToSearch, factory), options)
is PsiMethod -> is PsiMethod, is PsiParameter ->
/* Can't have KotlinPropertyFindUsagesOptions here since Kotlin properties do not override java methods, so
* elementsToSearch contains property declarations only */
HandlerAndOptions(JavaFindUsagesHandler(element, elementsToSearch.toTypedArray(), factory.javaHandlerFactory), HandlerAndOptions(JavaFindUsagesHandler(element, elementsToSearch.toTypedArray(), factory.javaHandlerFactory),
(options as KotlinFunctionFindUsagesOptions?)?.toJavaMethodOptions(project)) (options as KotlinCallableFindUsagesOptions?)?.toJavaOptions(project))
is PsiParameter ->
HandlerAndOptions(JavaFindUsagesHandler(element, elementsToSearch.toTypedArray(), factory.javaHandlerFactory),
(options as KotlinPropertyFindUsagesOptions?)?.toJavaVariableOptions(project))
else -> null else -> null
} }
@@ -21,13 +21,11 @@ import com.intellij.find.findUsages.AbstractFindUsagesDialog
import com.intellij.find.findUsages.FindUsagesOptions import com.intellij.find.findUsages.FindUsagesOptions
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference import com.intellij.psi.PsiReference
import com.intellij.psi.search.searches.MethodReferencesSearch import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import com.intellij.util.* import com.intellij.util.*
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.toLightMethods import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.idea.findUsages.KotlinCallableFindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.KotlinCallableFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
@@ -128,17 +126,14 @@ public abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration>
scope = options.searchScope, scope = options.searchScope,
kotlinOptions = createKotlinReferencesSearchOptions(options)) kotlinOptions = createKotlinReferencesSearchOptions(options))
val query = applyQueryFilters(element, options, ReferencesSearch.search(searchParameters)) with(applyQueryFilters(element, options, ReferencesSearch.search(searchParameters))) {
if (!query.forEach(referenceProcessor)) return false if (!forEach(referenceProcessor)) return false
val psiMethods = when (element) {
is PsiMethod -> listOf(element)
is KtFunction -> runReadAction { LightClassUtil.getLightClassMethods(element) }
else -> listOf<PsiMethod>()
} }
for (psiMethod in psiMethods) {
val query = applyQueryFilters(element, options, MethodReferencesSearch.search(psiMethod, options.searchScope, true)) for (psiMethod in runReadAction { element.toLightMethods() }) {
if (!query.forEach(referenceProcessor)) return false with(applyQueryFilters(element, options, MethodReferencesSearch.search(psiMethod, options.searchScope, true))) {
if (!forEach(referenceProcessor)) return false
}
} }
} }
@@ -16,31 +16,72 @@
package org.jetbrains.kotlin.idea.search.ideaExtensions package org.jetbrains.kotlin.idea.search.ideaExtensions
import com.intellij.openapi.application.QueryExecutorBase import com.intellij.psi.*
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.impl.search.MethodTextOccurrenceProcessor import com.intellij.psi.impl.search.MethodTextOccurrenceProcessor
import com.intellij.psi.impl.search.MethodUsagesSearcher import com.intellij.psi.impl.search.MethodUsagesSearcher
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.UsageSearchContext
import com.intellij.psi.search.searches.MethodReferencesSearch import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.util.Processor import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.KotlinLightMethod
import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod
import org.jetbrains.kotlin.asJava.toLightMethods import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.readWriteAccess
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.util.application.runReadActionInSmartMode
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.getPropertyNamesCandidatesByAccessorName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.utils.ifEmpty
public class KotlinOverridingMethodReferenceSearcher : MethodUsagesSearcher() { public class KotlinOverridingMethodReferenceSearcher : MethodUsagesSearcher() {
override fun processQuery(p: MethodReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
super.processQuery(p, consumer)
val method = p.method
p.project.runReadActionInSmartMode {
val containingClass = method.containingClass ?: return@runReadActionInSmartMode
val searchScope = p.effectiveSearchScope
.intersectWith(method.useScope)
.restrictToKotlinSources()
if (searchScope === GlobalSearchScope.EMPTY_SCOPE) return@runReadActionInSmartMode
for (name in getPropertyNamesCandidatesByAccessorName(Name.guess(method.name))) {
p.optimizer.searchWord(
name.asString(),
searchScope,
UsageSearchContext.IN_CODE,
true,
method,
getTextOccurrenceProcessor(arrayOf(method), containingClass, false)
)
}
}
}
override fun getTextOccurrenceProcessor(methods: Array<out PsiMethod>, override fun getTextOccurrenceProcessor(methods: Array<out PsiMethod>,
aClass: PsiClass, aClass: PsiClass,
strictSignatureSearch: Boolean): MethodTextOccurrenceProcessor? { strictSignatureSearch: Boolean): MethodTextOccurrenceProcessor {
return object: MethodTextOccurrenceProcessor(aClass, strictSignatureSearch, *methods) { return object: MethodTextOccurrenceProcessor(aClass, strictSignatureSearch, *methods) {
override fun processInexactReference(ref: PsiReference, refElement: PsiElement?, method: PsiMethod, consumer: Processor<PsiReference>): Boolean { override fun processInexactReference(ref: PsiReference, refElement: PsiElement?, method: PsiMethod, consumer: Processor<PsiReference>): Boolean {
if (refElement !is KtCallableDeclaration) return true if (refElement !is KtCallableDeclaration) return true
return refElement.getRepresentativeLightMethod()?.let { super.processInexactReference(ref, it, method, consumer) } ?: true
var lightMethods = refElement.toLightMethods()
.filterNot { it.hasModifierProperty(PsiModifier.FINAL) }
.ifEmpty { return true }
if (refElement is KtProperty || refElement is KtParameter) {
val isGetter = JvmAbi.isGetterName(method.name)
if (ref is KtSimpleNameReference) {
val readWriteAccess = ref.expression.readWriteAccess(true)
if (readWriteAccess.isRead != isGetter && readWriteAccess.isWrite == isGetter) return true
}
lightMethods = lightMethods.filter { JvmAbi.isGetterName(it.name) == isGetter }
}
return lightMethods.all { super.processInexactReference(ref, it, method, consumer) }
} }
} }
} }
@@ -19,14 +19,12 @@ package org.jetbrains.kotlin.idea.search.ideaExtensions
import com.intellij.openapi.application.QueryExecutorBase import com.intellij.openapi.application.QueryExecutorBase
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference import com.intellij.psi.PsiReference
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.UsageSearchContext import com.intellij.psi.search.UsageSearchContext
import com.intellij.psi.search.searches.MethodReferencesSearch import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.util.Processor import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.synthetic.JavaSyntheticPropertiesScope import org.jetbrains.kotlin.synthetic.JavaSyntheticPropertiesScope
@@ -37,7 +35,7 @@ public class KotlinPropertyAccessorsReferenceSearcher() : QueryExecutorBase<PsiR
val method = queryParameters.getMethod() val method = queryParameters.getMethod()
val propertyName = propertyName(method) ?: return val propertyName = propertyName(method) ?: return
val onlyKotlinFiles = restrictToKotlinSources(queryParameters.getEffectiveSearchScope()) val onlyKotlinFiles = queryParameters.getEffectiveSearchScope().restrictToKotlinSources()
queryParameters.getOptimizer()!!.searchWord( queryParameters.getOptimizer()!!.searchWord(
propertyName, propertyName,
@@ -58,11 +56,4 @@ public class KotlinPropertyAccessorsReferenceSearcher() : QueryExecutorBase<PsiR
val property = SyntheticJavaPropertyDescriptor.findByGetterOrSetter(functionDescriptor, syntheticExtensionsScope) ?: return null val property = SyntheticJavaPropertyDescriptor.findByGetterOrSetter(functionDescriptor, syntheticExtensionsScope) ?: return null
return property.getName().asString() return property.getName().asString()
} }
private fun restrictToKotlinSources(originalScope: SearchScope): SearchScope {
return when (originalScope) {
is GlobalSearchScope -> GlobalSearchScope.getScopeRestrictedByFileTypes(originalScope, KotlinFileType.INSTANCE)
else -> originalScope
}
}
} }
@@ -0,0 +1,29 @@
// PSI_ELEMENT: com.intellij.psi.PsiMethod
// OPTIONS: usages
public class J extends A {
@Override
public int <caret>getP() {
return 1;
}
@Override
public void setP(int value) {
}
}
class Test {
static void test() {
new A().getP();
new A().setP(1);
new AA().getP();
new AA().setP(1);
new J().getP();
new J().setP(1);
new B().getP();
new B().setP(1);
}
}
@@ -0,0 +1,25 @@
open class A {
open var p: Int = 1
}
class AA : A() {
override var p: Int = 1
}
class B : J() {
override var p: Int = 1
}
fun test() {
val t = A().p
A().p = 1
val t = AA().p
AA().p = 1
val t = J().p
J().p = 1
val t = B().p
B().p = 1
}
@@ -0,0 +1,4 @@
[javaPropertyGetterUsagesKJ.0.java] Unclassified usage (23: 17) new J().getP();
[javaPropertyGetterUsagesKJ.0.java] Unclassified usage (26: 17) new B().getP();
[javaPropertyGetterUsagesKJ.1.kt] Value read (20: 17) val t = J().p
[javaPropertyGetterUsagesKJ.1.kt] Value read (23: 17) val t = B().p
@@ -0,0 +1,29 @@
// PSI_ELEMENT: com.intellij.psi.PsiMethod
// OPTIONS: usages
public class J extends A {
@Override
public int getP() {
return 1;
}
@Override
public void <caret>setP(int value) {
}
}
class Test {
static void test() {
new A().getP();
new A().setP(1);
new AA().getP();
new AA().setP(1);
new J().getP();
new J().setP(1);
new B().getP();
new B().setP(1);
}
}
@@ -0,0 +1,25 @@
open class A {
open var p: Int = 1
}
class AA : A() {
override var p: Int = 1
}
class B : J() {
override var p: Int = 1
}
fun test() {
val t = A().p
A().p = 1
val t = AA().p
AA().p = 1
val t = J().p
J().p = 1
val t = B().p
B().p = 1
}
@@ -0,0 +1,4 @@
[javaPropertySetterUsagesKJ.0.java] Unclassified usage (24: 17) new J().setP(1);
[javaPropertySetterUsagesKJ.0.java] Unclassified usage (27: 17) new B().setP(1);
[javaPropertySetterUsagesKJ.1.kt] Value write (21: 9) J().p = 1
[javaPropertySetterUsagesKJ.1.kt] Value write (24: 9) B().p = 1
+2 -1
View File
@@ -1,8 +1,9 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtNamedFunction // PSI_ELEMENT: org.jetbrains.kotlin.psi.KtNamedFunction
// OPTIONS: usages // OPTIONS: usages
// FIND_BY_REF
class A(val n: Int) { class A(val n: Int) {
override fun <caret>equals(other: Any?): Boolean = other is A && other.n == n override fun equals(other: Any?): Boolean = other is A && other.n <caret>== n
} }
fun test() { fun test() {
@@ -1,4 +1,4 @@
Function call (10: 10) A(0) != A(1) Function call (10: 10) A(0) == A(1)
Function call (11: 10) A(0) equals A(1) Function call (11: 10) A(0) != A(1)
Function call (5: 71) override fun equals(other: Any?): Boolean = other is A && other.n == n Function call (12: 10) A(0) equals A(1)
Function call (9: 10) A(0) == A(1) Function call (6: 71) override fun equals(other: Any?): Boolean = other is A && other.n == n
@@ -6,11 +6,11 @@ interface A {
} }
class B: A { class B: A {
override fun <caret>foo() {} // Find usages gives no results override fun foo() {} // Find usages gives no results
} }
fun main(a: A) { fun main(a: A) {
a.foo() a.<caret>foo()
} }
// for KT-3769 Find usages gives no result for overrides // for KT-3769 Find usages gives no result for overrides
@@ -0,0 +1,27 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtProperty
// OPTIONS: usages
open class A {
open var <caret>p: Int = 1
}
class AA : A() {
override var p: Int = 1
}
class B : J() {
override var p: Int = 1
}
fun test() {
val t = A().p
A().p = 1
val t = AA().p
AA().p = 1
val t = J().p
J().p = 1
val t = B().p
B().p = 1
}
@@ -0,0 +1,27 @@
public class J extends A {
@Override
public int getP() {
return 1;
}
@Override
public void setP(int value) {
}
}
class Test {
static void test() {
new A().getP();
new A().setP(1);
new AA().getP();
new AA().setP(1);
new J().getP();
new J().setP(1);
new B().getP();
new B().setP(1);
}
}
@@ -0,0 +1,16 @@
[javaPropertyUsagesK.0.kt] Value read (16: 17) val t = A().p
[javaPropertyUsagesK.0.kt] Value read (19: 18) val t = AA().p
[javaPropertyUsagesK.0.kt] Value read (22: 17) val t = J().p
[javaPropertyUsagesK.0.kt] Value read (25: 17) val t = B().p
[javaPropertyUsagesK.0.kt] Value write (17: 9) A().p = 1
[javaPropertyUsagesK.0.kt] Value write (20: 10) AA().p = 1
[javaPropertyUsagesK.0.kt] Value write (23: 9) J().p = 1
[javaPropertyUsagesK.0.kt] Value write (26: 9) B().p = 1
[javaPropertyUsagesK.1.java] Unclassified usage (15: 17) new A().getP();
[javaPropertyUsagesK.1.java] Unclassified usage (16: 17) new A().setP(1);
[javaPropertyUsagesK.1.java] Unclassified usage (18: 18) new AA().getP();
[javaPropertyUsagesK.1.java] Unclassified usage (19: 18) new AA().setP(1);
[javaPropertyUsagesK.1.java] Unclassified usage (21: 17) new J().getP();
[javaPropertyUsagesK.1.java] Unclassified usage (22: 17) new J().setP(1);
[javaPropertyUsagesK.1.java] Unclassified usage (24: 17) new B().getP();
[javaPropertyUsagesK.1.java] Unclassified usage (25: 17) new B().setP(1);
@@ -0,0 +1,27 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtProperty
// OPTIONS: usages
open class A {
open var p: Int = 1
}
class AA : A() {
override var p: Int = 1
}
class B : J() {
override var <caret>p: Int = 1
}
fun test() {
val t = A().p
A().p = 1
val t = AA().p
AA().p = 1
val t = J().p
J().p = 1
val t = B().p
B().p = 1
}
@@ -0,0 +1,27 @@
public class J extends A {
@Override
public int getP() {
return 1;
}
@Override
public void setP(int value) {
}
}
class Test {
static void test() {
new A().getP();
new A().setP(1);
new AA().getP();
new AA().setP(1);
new J().getP();
new J().setP(1);
new B().getP();
new B().setP(1);
}
}
@@ -0,0 +1,4 @@
[javaPropertyUsagesKJK.0.kt] Value read (25: 17) val t = B().p
[javaPropertyUsagesKJK.0.kt] Value write (26: 9) B().p = 1
[javaPropertyUsagesKJK.1.java] Unclassified usage (24: 17) new B().getP();
[javaPropertyUsagesKJK.1.java] Unclassified usage (25: 17) new B().setP(1);
@@ -0,0 +1,27 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtProperty
// OPTIONS: usages
open class A {
open var p: Int = 1
}
class AA : A() {
override var <caret>p: Int = 1
}
class B : J() {
override var p: Int = 1
}
fun test() {
val t = A().p
A().p = 1
val t = AA().p
AA().p = 1
val t = J().p
J().p = 1
val t = B().p
B().p = 1
}
@@ -0,0 +1,27 @@
public class J extends A {
@Override
public int getP() {
return 1;
}
@Override
public void setP(int value) {
}
}
class Test {
static void test() {
new A().getP();
new A().setP(1);
new AA().getP();
new AA().setP(1);
new J().getP();
new J().setP(1);
new B().getP();
new B().setP(1);
}
}
@@ -0,0 +1,4 @@
[javaPropertyUsagesKK.0.kt] Value read (19: 18) val t = AA().p
[javaPropertyUsagesKK.0.kt] Value write (20: 10) AA().p = 1
[javaPropertyUsagesKK.1.java] Unclassified usage (18: 18) new AA().getP();
[javaPropertyUsagesKK.1.java] Unclassified usage (19: 18) new AA().setP(1);
@@ -34,7 +34,6 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*; import com.intellij.psi.*;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.LightProjectDescriptor;
@@ -53,6 +52,7 @@ import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions; import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions;
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory;
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions; import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions;
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions; import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions;
import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase; import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase;
@@ -433,8 +433,17 @@ public abstract class AbstractJetFindUsagesTest extends JetLightCodeInsightFixtu
protected Collection<UsageInfo> findUsages(@NotNull PsiElement targetElement, @Nullable FindUsagesOptions options) { protected Collection<UsageInfo> findUsages(@NotNull PsiElement targetElement, @Nullable FindUsagesOptions options) {
Project project = getProject(); Project project = getProject();
FindUsagesHandler handler =
((FindManagerImpl) FindManager.getInstance(project)).getFindUsagesManager().getFindUsagesHandler(targetElement, false); FindUsagesHandler handler;
if (targetElement instanceof PsiMember) {
handler = new JavaFindUsagesHandler(targetElement, new JavaFindUsagesHandlerFactory(project));
}
else if (targetElement instanceof KtDeclaration) {
handler = new KotlinFindUsagesHandlerFactory(project).createFindUsagesHandlerNoQuestions(targetElement);
}
else {
handler = ((FindManagerImpl) FindManager.getInstance(project)).getFindUsagesManager().getFindUsagesHandler(targetElement, false);
}
assert handler != null : "Cannot find handler for: " + targetElement; assert handler != null : "Cannot find handler for: " + targetElement;
if (options == null) { if (options == null) {
@@ -685,6 +685,33 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest {
} }
} }
@TestMetadata("idea/testData/findUsages/kotlin/findJavaPropertyUsages")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FindJavaPropertyUsages extends AbstractJetFindUsagesTest {
public void testAllFilesPresentInFindJavaPropertyUsages() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/findUsages/kotlin/findJavaPropertyUsages"), Pattern.compile("^(.+)\\.0\\.kt$"), true);
}
@TestMetadata("javaPropertyUsagesK.0.kt")
public void testJavaPropertyUsagesK() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findJavaPropertyUsages/javaPropertyUsagesK.0.kt");
doTest(fileName);
}
@TestMetadata("javaPropertyUsagesKJK.0.kt")
public void testJavaPropertyUsagesKJK() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findJavaPropertyUsages/javaPropertyUsagesKJK.0.kt");
doTest(fileName);
}
@TestMetadata("javaPropertyUsagesKK.0.kt")
public void testJavaPropertyUsagesKK() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findJavaPropertyUsages/javaPropertyUsagesKK.0.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/findUsages/kotlin/findObjectUsages") @TestMetadata("idea/testData/findUsages/kotlin/findObjectUsages")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -1341,6 +1368,27 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest {
doTest(fileName); doTest(fileName);
} }
} }
@TestMetadata("idea/testData/findUsages/java/findJavaPropertyUsages")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FindJavaPropertyUsages extends AbstractJetFindUsagesTest {
public void testAllFilesPresentInFindJavaPropertyUsages() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/findUsages/java/findJavaPropertyUsages"), Pattern.compile("^(.+)\\.0\\.java$"), true);
}
@TestMetadata("javaPropertyGetterUsagesKJ.0.java")
public void testJavaPropertyGetterUsagesKJ() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/java/findJavaPropertyUsages/javaPropertyGetterUsagesKJ.0.java");
doTest(fileName);
}
@TestMetadata("javaPropertySetterUsagesKJ.0.java")
public void testJavaPropertySetterUsagesKJ() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/java/findJavaPropertyUsages/javaPropertySetterUsagesKJ.0.java");
doTest(fileName);
}
}
} }
@TestMetadata("idea/testData/findUsages/propertyFiles") @TestMetadata("idea/testData/findUsages/propertyFiles")