Initial implementation of faster component function usage search
This commit is contained in:
@@ -16,10 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.psi;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface KtWithExpressionInitializer extends PsiElement {
|
||||
public interface KtWithExpressionInitializer extends KtDeclaration {
|
||||
@Nullable
|
||||
KtExpression getInitializer();
|
||||
|
||||
|
||||
@@ -20,8 +20,11 @@ import org.jetbrains.kotlin.name.Name
|
||||
|
||||
private val COMPONENT_FUNCTION_NAME_PREFIX = "component"
|
||||
|
||||
fun isComponentLike(name: Name): Boolean {
|
||||
if (!name.asString().startsWith(COMPONENT_FUNCTION_NAME_PREFIX)) return false
|
||||
fun isComponentLike(name: Name): Boolean
|
||||
= isComponentLike(name.asString())
|
||||
|
||||
fun isComponentLike(name: String): Boolean {
|
||||
if (!name.startsWith(COMPONENT_FUNCTION_NAME_PREFIX)) return false
|
||||
|
||||
try {
|
||||
getComponentIndex(name)
|
||||
@@ -33,8 +36,8 @@ fun isComponentLike(name: Name): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
fun getComponentIndex(componentName: Name): Int =
|
||||
componentName.asString().substring(COMPONENT_FUNCTION_NAME_PREFIX.length).toInt()
|
||||
fun getComponentIndex(componentName: String): Int
|
||||
= componentName.substring(COMPONENT_FUNCTION_NAME_PREFIX.length).toInt()
|
||||
|
||||
fun createComponentName(index: Int): Name =
|
||||
Name.identifier(COMPONENT_FUNCTION_NAME_PREFIX + index)
|
||||
fun createComponentName(index: Int): Name
|
||||
= Name.identifier(COMPONENT_FUNCTION_NAME_PREFIX + index)
|
||||
|
||||
+34
-18
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.idea.search.allScope
|
||||
import org.jetbrains.kotlin.idea.search.effectiveSearchScope
|
||||
import org.jetbrains.kotlin.idea.search.unionSafe
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.findDestructuringDeclarationUsages
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.getClassNameForCompanionObject
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.getSpecialNamesToSearch
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
@@ -45,6 +46,7 @@ import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||
|
||||
data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean = false,
|
||||
val acceptOverloads: Boolean = false,
|
||||
@@ -96,8 +98,8 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
||||
|
||||
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement, filter = refFilter, options = kotlinOptions)
|
||||
|
||||
val name = runReadAction { unwrappedElement.name }
|
||||
if (kotlinOptions.anyEnabled()) {
|
||||
val name = runReadAction { unwrappedElement.name }
|
||||
if (name != null) {
|
||||
queryParameters.optimizer.searchWord(name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, unwrappedElement,
|
||||
resultProcessor)
|
||||
@@ -114,7 +116,32 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
||||
}
|
||||
|
||||
if (!(unwrappedElement is KtElement && isOnlyKotlinSearch(effectiveSearchScope))) {
|
||||
searchLightElements(queryParameters, element)
|
||||
searchLightElements(queryParameters, element, consumer)
|
||||
}
|
||||
|
||||
if (kotlinOptions.searchForComponentConventions) {
|
||||
when (element) {
|
||||
is KtFunction -> {
|
||||
if (name != null && isComponentLike(name)) {
|
||||
findDestructuringDeclarationUsages(element, effectiveSearchScope, consumer, queryParameters.optimizer)
|
||||
}
|
||||
}
|
||||
|
||||
is KtParameter -> {
|
||||
val componentFunctionDescriptor = element.dataClassComponentFunction()
|
||||
if (componentFunctionDescriptor != null) {
|
||||
val containingClass = element.getStrictParentOfType<KtClassOrObject>()?.toLightClass()
|
||||
searchDataClassComponentUsages(queryParameters, containingClass, componentFunctionDescriptor, consumer)
|
||||
}
|
||||
}
|
||||
|
||||
is KtLightParameter -> {
|
||||
val componentFunctionDescriptor = element.kotlinOrigin?.dataClassComponentFunction()
|
||||
if (componentFunctionDescriptor != null) {
|
||||
searchDataClassComponentUsages(queryParameters, element.method.containingClass, componentFunctionDescriptor, consumer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,16 +236,19 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
||||
|
||||
private fun searchDataClassComponentUsages(queryParameters: ReferencesSearch.SearchParameters,
|
||||
containingClass: PsiClass?,
|
||||
componentFunctionDescriptor: FunctionDescriptor) {
|
||||
componentFunctionDescriptor: FunctionDescriptor,
|
||||
consumer: Processor<PsiReference>
|
||||
) {
|
||||
val componentFunction = containingClass?.methods?.find {
|
||||
it.name == componentFunctionDescriptor.name.asString() && it.parameterList.parametersCount == 0
|
||||
}
|
||||
if (componentFunction != null) {
|
||||
searchNamedElement(queryParameters, componentFunction)
|
||||
findDestructuringDeclarationUsages(componentFunction, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchLightElements(queryParameters: ReferencesSearch.SearchParameters, element: PsiElement) {
|
||||
private fun searchLightElements(queryParameters: ReferencesSearch.SearchParameters, element: PsiElement, consumer: Processor<PsiReference>) {
|
||||
when (element) {
|
||||
is KtClassOrObject -> processKtClassOrObject(element, queryParameters)
|
||||
is KtNamedFunction, is KtSecondaryConstructor -> {
|
||||
@@ -242,14 +272,6 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
||||
|
||||
is KtParameter -> {
|
||||
searchPropertyMethods(queryParameters, element)
|
||||
runReadAction {
|
||||
|
||||
val componentFunctionDescriptor = element.dataClassComponentFunction()
|
||||
if (componentFunctionDescriptor != null) {
|
||||
val containingClass = element.getStrictParentOfType<KtClassOrObject>()?.toLightClass()
|
||||
searchDataClassComponentUsages(queryParameters, containingClass, componentFunctionDescriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtLightMethod -> {
|
||||
@@ -269,12 +291,6 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
||||
|
||||
is KtLightParameter -> {
|
||||
val origin = element.kotlinOrigin ?: return
|
||||
runReadAction {
|
||||
val componentFunctionDescriptor = origin.dataClassComponentFunction()
|
||||
if (componentFunctionDescriptor != null) {
|
||||
searchDataClassComponentUsages(queryParameters, element.method.containingClass, componentFunctionDescriptor)
|
||||
}
|
||||
}
|
||||
searchPropertyMethods(queryParameters, origin)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
package org.jetbrains.kotlin.idea.search.usagesSearch
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import org.jetbrains.kotlin.idea.references.*
|
||||
import org.jetbrains.kotlin.idea.references.KtArrayAccessReference
|
||||
import org.jetbrains.kotlin.idea.references.KtForLoopInReference
|
||||
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DelegatedPropertyResolver
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions.*
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
@@ -57,8 +59,6 @@ fun Name.getOperationSymbolsToSearch(): Pair<Set<KtToken>, Class<*>?> {
|
||||
DelegatedPropertyResolver.PROPERTY_DELEGATED_FUNCTION_NAME -> return setOf(KtTokens.BY_KEYWORD) to KtPropertyDelegationMethodsReference::class.java
|
||||
}
|
||||
|
||||
if (isComponentLike(this)) return setOf(KtTokens.LPAR) to KtDestructuringDeclarationReference::class.java
|
||||
|
||||
val unaryOp = UNARY_OPERATION_NAMES_WITH_DEPRECATED_INVERTED[this]
|
||||
if (unaryOp != null) return setOf(unaryOp) to KtSimpleNameReference::class.java
|
||||
|
||||
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* 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.idea.search.usagesSearch
|
||||
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.*
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinRequestResultProcessor
|
||||
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
|
||||
import org.jetbrains.kotlin.idea.util.toFuzzyType
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.util.isValidOperator
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import java.util.*
|
||||
|
||||
//TODO: compiled code
|
||||
//TODO: check if it's too expensive
|
||||
|
||||
fun findDestructuringDeclarationUsages(
|
||||
componentFunction: PsiMethod,
|
||||
scope: SearchScope,
|
||||
consumer: Processor<PsiReference>,
|
||||
optimizer: SearchRequestCollector
|
||||
) {
|
||||
if (componentFunction !is KtLightMethod) return //TODO
|
||||
val ktDeclarationTarget = componentFunction.kotlinOrigin as? KtDeclaration ?: return //TODO?
|
||||
findDestructuringDeclarationUsages(ktDeclarationTarget, scope, consumer, optimizer)
|
||||
}
|
||||
|
||||
fun findDestructuringDeclarationUsages(
|
||||
ktDeclaration: KtDeclaration,
|
||||
scope: SearchScope,
|
||||
consumer: Processor<PsiReference>,
|
||||
optimizer: SearchRequestCollector
|
||||
) {
|
||||
// for local scope it's faster to use plain search
|
||||
if (scope is LocalSearchScope) {
|
||||
val unwrappedElement = ktDeclaration.namedUnwrappedElement ?: return
|
||||
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement,
|
||||
filter = { ref -> ref is KtDestructuringDeclarationReference })
|
||||
optimizer.searchWord("(", scope.restrictToKotlinSources(), UsageSearchContext.IN_CODE, true, unwrappedElement, resultProcessor)
|
||||
return
|
||||
}
|
||||
|
||||
val descriptor = ktDeclaration.resolveToDescriptor() as? CallableDescriptor ?: return
|
||||
|
||||
if (descriptor is FunctionDescriptor && !descriptor.isValidOperator()) return
|
||||
|
||||
val dataType = if (descriptor.isExtension) {
|
||||
descriptor.fuzzyExtensionReceiverType()!!
|
||||
}
|
||||
else {
|
||||
val classDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return
|
||||
classDescriptor.defaultType.toFuzzyType(classDescriptor.typeConstructor.parameters)
|
||||
}
|
||||
|
||||
Processor(dataType, ktDeclaration, scope, consumer).run()
|
||||
}
|
||||
|
||||
private class Processor(
|
||||
private val dataType: FuzzyType,
|
||||
private val target: KtDeclaration,
|
||||
private val searchScope: SearchScope,
|
||||
private val consumer: Processor<PsiReference>
|
||||
) {
|
||||
private val project = target.project
|
||||
private val declarationsToSearch = ArrayList<PsiElement>()
|
||||
private val declarationsToSearchSet = HashSet<PsiElement>()
|
||||
|
||||
fun run() {
|
||||
val dataClassDescriptor = dataType.type.constructor.declarationDescriptor ?: return
|
||||
val dataClassDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, dataClassDescriptor)
|
||||
val psiClass = when (dataClassDeclaration) {
|
||||
is PsiClass -> dataClassDeclaration
|
||||
is KtClassOrObject -> dataClassDeclaration.toLightClass() ?: return
|
||||
else -> return
|
||||
}
|
||||
|
||||
val parameters = ClassInheritorsSearch.SearchParameters(psiClass, GlobalSearchScope.allScope(project), true, true, false)
|
||||
val classesToSearch = listOf(psiClass) + ClassInheritorsSearch.search(parameters).findAll()
|
||||
|
||||
for (classToSearch in classesToSearch) {
|
||||
ReferencesSearch.search(classToSearch).forEach(Processor { reference ->
|
||||
if (!processDataClassUsage(reference)) {
|
||||
//TODO
|
||||
val element = reference.element
|
||||
val document = PsiDocumentManager.getInstance(project).getDocument(element.containingFile)
|
||||
val lineAndCol = DiagnosticUtils.offsetToLineAndColumn(document, element.startOffset)
|
||||
TODO("Unsupported reference: '${element.text}' in ${element.containingFile.name} line ${lineAndCol.line} column ${lineAndCol.column}")
|
||||
}
|
||||
else {
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// we use index instead of iterator because elements are added during iteration
|
||||
var index = 0
|
||||
while (index < declarationsToSearch.size) {
|
||||
val declaration = declarationsToSearch[index++]
|
||||
processDeclarationOfTypeWithDataClass(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: check if it's operator (too expensive)
|
||||
private fun addDeclarationToProcess(declaration: PsiElement) {
|
||||
if (declarationsToSearchSet.add(declaration)) {
|
||||
declarationsToSearch.add(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process usage of our data class or one of its inheritors
|
||||
*/
|
||||
private fun processDataClassUsage(reference: PsiReference): Boolean {
|
||||
val element = reference.element
|
||||
return when (element.language) {
|
||||
KotlinLanguage.INSTANCE -> processKotlinDataClassUsage(element)
|
||||
|
||||
JavaLanguage.INSTANCE -> processJavaDataClassUsage(element)
|
||||
|
||||
else -> false // we don't know anything about usages in other languages - so we downgrade to slow algorithm in this case
|
||||
}
|
||||
}
|
||||
|
||||
private fun processKotlinDataClassUsage(element: PsiElement): Boolean {
|
||||
//TODO: type aliases
|
||||
//TODO: doc-comments
|
||||
|
||||
when (element) {
|
||||
is KtReferenceExpression -> {
|
||||
val parent = element.parent
|
||||
when (parent) {
|
||||
is KtUserType -> {
|
||||
val typeRef = parent.parents.lastOrNull { it is KtTypeReference }
|
||||
val typeRefParent = typeRef?.parent
|
||||
when (typeRefParent) {
|
||||
is KtCallableDeclaration -> {
|
||||
when (typeRef) {
|
||||
typeRefParent.typeReference -> {
|
||||
addDeclarationToProcess(typeRefParent)
|
||||
return true
|
||||
}
|
||||
|
||||
typeRefParent.receiverTypeReference -> {
|
||||
//TODO: search usages inside extensions and member functions of our class & derived
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtTypeProjection -> {
|
||||
val callExpression = (typeRefParent.parent as? KtTypeArgumentList)?.parent as? KtCallExpression
|
||||
if (callExpression != null) {
|
||||
processSuspiciousExpression(callExpression)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
is KtConstructorCalleeExpression -> {
|
||||
if (typeRefParent.parent is KtSuperTypeCallEntry) {
|
||||
// usage in super type list - just ignore, inheritors are processed above
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is KtCallExpression -> {
|
||||
if (element == parent.calleeExpression) {
|
||||
processSuspiciousExpression(parent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (element.getStrictParentOfType<KtImportDirective>() != null) return true // ignore usage in import
|
||||
}
|
||||
}
|
||||
|
||||
return false // unsupported type of reference
|
||||
}
|
||||
|
||||
private fun processJavaDataClassUsage(element: PsiElement): Boolean {
|
||||
if (element !is PsiJavaCodeReferenceElement) return true // meaningless reference from Java
|
||||
|
||||
ParentsLoop@
|
||||
for (parent in element.parents) {
|
||||
when (parent) {
|
||||
is PsiCodeBlock,
|
||||
is PsiExpression,
|
||||
is PsiImportStatement,
|
||||
is PsiParameter ->
|
||||
break@ParentsLoop // ignore local usages, usages in imports and in parameter types
|
||||
|
||||
is PsiMethod, is PsiField -> {
|
||||
if (!(parent as PsiModifierListOwner).isPrivateOrLocal()) {
|
||||
addDeclarationToProcess(parent)
|
||||
}
|
||||
break@ParentsLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Process declaration whose type is our data class (or data class used anywhere inside that type)
|
||||
*/
|
||||
private fun processDeclarationOfTypeWithDataClass(declaration: PsiElement) {
|
||||
// we don't need to search usages of declarations in Java because Java doesn't have implicitly typed declarations so such usages cannot affect Kotlin code
|
||||
//TODO: what about Scala and other JVM-languages?
|
||||
val declarationUsageScope = GlobalSearchScope.projectScope(declaration.project).restrictToKotlinSources()
|
||||
ReferencesSearch.search(declaration, declarationUsageScope).forEach { reference ->
|
||||
(reference.element as? KtReferenceExpression)?.let { processSuspiciousExpression(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process expression which may have type of our data class (or data class used anywhere inside that type)
|
||||
*/
|
||||
private fun processSuspiciousExpression(expression: KtExpression) {
|
||||
ParentsLoop@
|
||||
for (container in expression.parentsWithSelf) {
|
||||
if (container !is KtExpression) continue
|
||||
val parent = container.parent
|
||||
|
||||
when (parent) {
|
||||
is KtDestructuringDeclaration -> {
|
||||
processSuspiciousDeclaration(parent)
|
||||
break@ParentsLoop
|
||||
}
|
||||
|
||||
is KtWithExpressionInitializer -> {
|
||||
if (container != parent.initializer) continue@ParentsLoop
|
||||
|
||||
processSuspiciousDeclaration(parent)
|
||||
|
||||
break@ParentsLoop
|
||||
}
|
||||
|
||||
is KtContainerNode -> {
|
||||
if (parent.node.elementType == KtNodeTypes.LOOP_RANGE) {
|
||||
val forExpression = parent.parent as KtForExpression
|
||||
(forExpression.destructuringParameter ?: forExpression.loopParameter as KtDeclaration?)?.let {
|
||||
processSuspiciousDeclaration(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: entry of data type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process declaration which may have implicit type of our data class (or data class used anywhere inside that type)
|
||||
*/
|
||||
private fun processSuspiciousDeclaration(declaration: KtDeclaration) {
|
||||
if (declaration is KtDestructuringDeclaration) {
|
||||
if (searchScope.contains(declaration)) {
|
||||
val declarationReference = declaration.references.firstIsInstance<KtDestructuringDeclarationReference>()
|
||||
if (declarationReference.isReferenceTo(target)) {
|
||||
consumer.process(declarationReference)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!isImplicitlyTyped(declaration)) return
|
||||
|
||||
val descriptor = declaration.resolveToDescriptorIfAny() as? CallableDescriptor ?: return
|
||||
val type = descriptor.returnType
|
||||
if (type != null && type.containsTypeOrDerivedInside(dataType)) {
|
||||
addDeclarationToProcess(declaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun PsiModifierListOwner.isPrivateOrLocal(): Boolean {
|
||||
return hasModifierProperty(PsiModifier.PRIVATE) || parents.any { it is PsiCodeBlock }
|
||||
}
|
||||
|
||||
private fun KotlinType.containsTypeOrDerivedInside(type: FuzzyType): Boolean {
|
||||
return type.checkIsSuperTypeOf(this) != null || arguments.any { it.type.containsTypeOrDerivedInside(type) }
|
||||
}
|
||||
|
||||
private fun isImplicitlyTyped(declaration: KtDeclaration): Boolean {
|
||||
return when (declaration) {
|
||||
is KtFunction -> !declaration.hasDeclaredReturnType()
|
||||
is KtVariableDeclaration -> declaration.typeReference == null
|
||||
is KtParameter -> declaration.typeReference == null
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,8 @@ import org.jetbrains.kotlin.asJava.LightClassUtil.PropertyAccessorsPsiMethods
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
@@ -69,14 +67,7 @@ fun PsiNamedElement.getSpecialNamesToSearch(options: KotlinReferencesSearchOptio
|
||||
val name = name
|
||||
return when {
|
||||
name == null || !Name.isValidIdentifier(name) -> Collections.emptyList<String>() to null
|
||||
this is KtParameter -> {
|
||||
if (!options.searchForComponentConventions) return Collections.emptyList<String>() to null
|
||||
|
||||
val componentFunctionName = this.dataClassComponentFunction()?.name
|
||||
if (componentFunctionName == null) return Collections.emptyList<String>() to null
|
||||
|
||||
return listOf(componentFunctionName.asString(), KtTokens.LPAR.value) to KtDestructuringDeclarationReference::class.java
|
||||
}
|
||||
else -> {
|
||||
val operationSymbolsToSearch = Name.identifier(name).getOperationSymbolsToSearch()
|
||||
operationSymbolsToSearch.first.map { (it as KtSingleValueToken).value } to operationSymbolsToSearch.second
|
||||
|
||||
@@ -46,6 +46,7 @@ import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
@@ -158,10 +159,10 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
|
||||
if (declaration is KtProperty && declaration.isLocal) return
|
||||
if (declaration is KtParameter && (declaration.getParent()?.parent !is KtPrimaryConstructor || !declaration.hasValOrVar())) return
|
||||
if (declaration is KtNamedFunction && isConventionalName(declaration)) return
|
||||
|
||||
// More expensive, resolve-based checks
|
||||
declaration.resolveToDescriptorIfAny() ?: return
|
||||
val descriptor = declaration.resolveToDescriptorIfAny() ?: return
|
||||
if (descriptor is FunctionDescriptor && descriptor.isOperator) return
|
||||
if (isEntryPoint(declaration)) return
|
||||
if (declaration is KtProperty && declaration.isSerializationImplicitlyUsedField()) return
|
||||
if (declaration is KtNamedFunction && declaration.isSerializationImplicitlyUsedMethod()) return
|
||||
@@ -205,11 +206,6 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
||||
return hasTextUsages
|
||||
}
|
||||
|
||||
private fun isConventionalName(namedDeclaration: KtNamedDeclaration): Boolean {
|
||||
val name = namedDeclaration.nameAsName
|
||||
return name!!.getOperationSymbolsToSearch().first.isNotEmpty() || name == OperatorNameConventions.INVOKE
|
||||
}
|
||||
|
||||
private fun hasNonTrivialUsages(declaration: KtNamedDeclaration): Boolean {
|
||||
val psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(declaration.project)
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType) : Kotli
|
||||
companion object {
|
||||
fun getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(diagnostic: Diagnostic): KtDestructuringDeclarationEntry {
|
||||
val componentName = COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.cast(diagnostic).a
|
||||
val componentIndex = getComponentIndex(componentName)
|
||||
val componentIndex = getComponentIndex(componentName.asString())
|
||||
val multiDeclaration = QuickFixUtil.getParentElementOfType(diagnostic, KtDestructuringDeclaration::class.java) ?: error("COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH reported on expression that is not within any multi declaration")
|
||||
return multiDeclaration.entries[componentIndex - 1]
|
||||
}
|
||||
|
||||
+2
-2
@@ -22,8 +22,8 @@ import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.getComponentIndex
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
@@ -40,7 +40,7 @@ object CreateComponentFunctionActionFactory : CreateCallableMemberFromUsageFacto
|
||||
val name = diagnosticWithParameters.a
|
||||
if (!isComponentLike(name)) return null
|
||||
|
||||
val componentNumber = getComponentIndex(name) - 1
|
||||
val componentNumber = getComponentIndex(name.asString()) - 1
|
||||
|
||||
val ownerType = element.initializer?.let { TypeInfo(it, Variance.IN_VARIANCE) }
|
||||
?: TypeInfo(diagnosticWithParameters.b, Variance.IN_VARIANCE)
|
||||
|
||||
+18
-9
@@ -22,26 +22,35 @@ import com.intellij.psi.search.UsageSearchContext
|
||||
import com.intellij.psi.search.searches.MethodReferencesSearch
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.findDestructuringDeclarationUsages
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.getOperationSymbolsToSearch
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||
|
||||
class KotlinConventionMethodReferencesSearcher() : QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters>(true) {
|
||||
override fun processQuery(queryParameters: MethodReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
|
||||
val method = queryParameters.method
|
||||
val name = runReadAction { method.name }
|
||||
if (!Name.isValidIdentifier(name)) return
|
||||
val operationSymbolsToSearch = Name.identifier(name).getOperationSymbolsToSearch()
|
||||
val wordsToSearch = operationSymbolsToSearch.first.map { (it as KtSingleValueToken).value }
|
||||
if (wordsToSearch.isEmpty()) return
|
||||
val resultProcessor = KotlinRequestResultProcessor(method,
|
||||
filter = { ref -> ref.javaClass == operationSymbolsToSearch.second })
|
||||
val identifier = Name.identifier(name)
|
||||
|
||||
wordsToSearch.forEach { word ->
|
||||
queryParameters.optimizer.searchWord(word, queryParameters.effectiveSearchScope.restrictToKotlinSources(),
|
||||
UsageSearchContext.IN_CODE, true, method,
|
||||
resultProcessor)
|
||||
if (isComponentLike(identifier)) {
|
||||
findDestructuringDeclarationUsages(method, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer)
|
||||
}
|
||||
else {
|
||||
val operationSymbolsToSearch = identifier.getOperationSymbolsToSearch()
|
||||
val wordsToSearch = operationSymbolsToSearch.first.map { (it as KtSingleValueToken).value }
|
||||
if (wordsToSearch.isEmpty()) return
|
||||
val resultProcessor = KotlinRequestResultProcessor(method,
|
||||
filter = { ref -> ref.javaClass == operationSymbolsToSearch.second })
|
||||
|
||||
wordsToSearch.forEach { word ->
|
||||
queryParameters.optimizer.searchWord(word, queryParameters.effectiveSearchScope.restrictToKotlinSources(),
|
||||
UsageSearchContext.IN_CODE, true, method,
|
||||
resultProcessor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
fun test() {
|
||||
for ((x, y, z) in arrayOf<A>()) {
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
[componentFunctions.0.kt] Function call 9 a.component1()
|
||||
[componentFunctions.0.kt] Value read 10 val (x, y, z) = a
|
||||
[componentFunctions.0.kt] Value read 8 a.n
|
||||
[componentFunctions.1.kt] Value read 2 for ((x, y, z) in arrayOf<A>()) {
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtFunction
|
||||
// OPTIONS: usages
|
||||
|
||||
class X<T>
|
||||
|
||||
operator fun <T> X<T>.<caret>component1(): Int = 0
|
||||
operator fun <T> X<T>.component2(): Int = 0
|
||||
|
||||
fun f() = X<String>()
|
||||
|
||||
fun test() {
|
||||
val (x, y) = f()
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Value read 12 val (x, y) = f()
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtFunction
|
||||
// OPTIONS: usages
|
||||
|
||||
class X<T>
|
||||
|
||||
operator fun X<Int>.<caret>component1(): Int = 0
|
||||
operator fun X<Int>.component2(): Int = 0
|
||||
|
||||
operator fun X<String>.component1(): Int = 0
|
||||
operator fun X<String>.component2(): Int = 0
|
||||
|
||||
fun f() = X<Int>()
|
||||
fun g() = X<String>()
|
||||
|
||||
fun test() {
|
||||
val (x1, y1) = f()
|
||||
val (x2, y2) = g()
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Value read 16 val (x1, y1) = f()
|
||||
@@ -0,0 +1,11 @@
|
||||
import java.util.List;
|
||||
import pack.A;
|
||||
|
||||
class JavaClass {
|
||||
public List<A> getA() {
|
||||
A a = new A(1, "", "");
|
||||
return Collections.singletonList(a);
|
||||
}
|
||||
|
||||
public void takeA(A a){}
|
||||
}
|
||||
+1
-7
@@ -1,11 +1,5 @@
|
||||
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtParameter
|
||||
// OPTIONS: usages
|
||||
package pack
|
||||
|
||||
data class A(val <caret>n: Int, val s: String, val o: Any)
|
||||
|
||||
fun test() {
|
||||
val a = A(1, "2", Any())
|
||||
a.n
|
||||
a.component1()
|
||||
val (x, y, z) = a
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import pack.A
|
||||
|
||||
fun test() {
|
||||
for ((x, y, z) in arrayOf<A>()) {
|
||||
}
|
||||
|
||||
for (a in listOf<A>()) {
|
||||
val (x, y) = a
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import pack.A
|
||||
|
||||
fun test(javaClass: JavaClass) {
|
||||
val a = A(1, "2", Any())
|
||||
a.n
|
||||
a.component1()
|
||||
|
||||
val (x, y, z) = a
|
||||
val (x1, y1, z1) = f()
|
||||
val (x2, y2, z2) = g()
|
||||
val (x3, y3, z3) = h()
|
||||
|
||||
val (x4, y4, z4) = listOfA()[0]
|
||||
|
||||
val (x5, y5, z5) = javaClass.getA()[0]
|
||||
}
|
||||
|
||||
fun f(): A = TODO()
|
||||
fun g() = A()
|
||||
fun h() = g()
|
||||
|
||||
fun listOfA() = listOf<A>(A(1, "", ""))
|
||||
@@ -0,0 +1,10 @@
|
||||
[dataClass.1.kt] Value read 4 for ((x, y, z) in arrayOf<A>()) {
|
||||
[dataClass.1.kt] Value read 8 val (x, y) = a
|
||||
[dataClass.2.kt] Function call 6 a.component1()
|
||||
[dataClass.2.kt] Value read 10 val (x2, y2, z2) = g()
|
||||
[dataClass.2.kt] Value read 11 val (x3, y3, z3) = h()
|
||||
[dataClass.2.kt] Value read 13 val (x4, y4, z4) = listOfA()[0]
|
||||
[dataClass.2.kt] Value read 15 val (x5, y5, z5) = javaClass.getA()[0]
|
||||
[dataClass.2.kt] Value read 5 a.n
|
||||
[dataClass.2.kt] Value read 8 val (x, y, z) = a
|
||||
[dataClass.2.kt] Value read 9 val (x1, y1, z1) = f()
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtFunction
|
||||
// OPTIONS: usages
|
||||
|
||||
class X
|
||||
|
||||
operator fun X.component1(): Int = 0
|
||||
operator fun X.<caret>component2(): Int = 1
|
||||
|
||||
|
||||
fun f() = X()
|
||||
|
||||
fun test() {
|
||||
val (x, y) = f()
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Value read 13 val (x, y) = f()
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtFunction
|
||||
// OPTIONS: usages
|
||||
|
||||
open class X {
|
||||
operator fun <caret>component1(): Int = 0
|
||||
operator fun component2(): Int = 1
|
||||
}
|
||||
|
||||
open class Y : X()
|
||||
open class Z : Y()
|
||||
|
||||
fun f() = X()
|
||||
fun g() = Y()
|
||||
fun h() = Z()
|
||||
|
||||
fun test() {
|
||||
val (x, y) = f()
|
||||
val (x1, y1) = g()
|
||||
val (x2, y2) = h()
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
Value read 17 val (x, y) = f()
|
||||
Value read 18 val (x1, y1) = g()
|
||||
Value read 19 val (x2, y2) = h()
|
||||
@@ -1,34 +0,0 @@
|
||||
// all these are unused, but have conventional names, so they won't be marked as unused
|
||||
|
||||
fun inc() {
|
||||
}
|
||||
|
||||
fun dec() {
|
||||
}
|
||||
|
||||
fun component1() {
|
||||
}
|
||||
|
||||
fun component100() {
|
||||
}
|
||||
|
||||
fun equals() {
|
||||
}
|
||||
|
||||
fun invoke() {
|
||||
}
|
||||
|
||||
fun iterator() {
|
||||
}
|
||||
|
||||
fun compareTo() {
|
||||
}
|
||||
|
||||
fun get() {
|
||||
}
|
||||
|
||||
fun set() {
|
||||
}
|
||||
|
||||
fun plusAssign() {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// all these are unused but are operators
|
||||
|
||||
operator fun String.inc() = ""
|
||||
|
||||
operator fun String.dec() = ""
|
||||
|
||||
operator fun String.component1() = 0
|
||||
|
||||
operator fun String.component100() = 0
|
||||
@@ -0,0 +1,10 @@
|
||||
class A
|
||||
|
||||
operator fun A.<caret>component1(): Int = 0
|
||||
operator fun A.component2(): Int = 1
|
||||
|
||||
fun test() {
|
||||
val a = A()
|
||||
a.component1()
|
||||
val (x, y) = a
|
||||
}
|
||||
@@ -84,18 +84,6 @@ public class FindUsagesTestGenerated extends AbstractFindUsagesTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("componentFunctions.0.kt")
|
||||
public void testComponentFunctions() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/componentFunctions.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("componentFunctionsByRef.0.kt")
|
||||
public void testComponentFunctionsByRef() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/componentFunctionsByRef.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("contains.0.kt")
|
||||
public void testContains() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/contains.0.kt");
|
||||
@@ -173,6 +161,51 @@ public class FindUsagesTestGenerated extends AbstractFindUsagesTest {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/unaryMinus.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/findUsages/kotlin/conventions/components")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Components extends AbstractFindUsagesTest {
|
||||
public void testAllFilesPresentInComponents() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/findUsages/kotlin/conventions/components"), Pattern.compile("^(.+)\\.0\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("componentFunForGenericType1.0.kt")
|
||||
public void testComponentFunForGenericType1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/componentFunForGenericType1.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("componentFunForGenericType2.0.kt")
|
||||
public void testComponentFunForGenericType2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/componentFunForGenericType2.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("dataClass.0.kt")
|
||||
public void testDataClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/dataClass.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("dataClassComponentByRef.0.kt")
|
||||
public void testDataClassComponentByRef() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/dataClassComponentByRef.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extensionComponentFun.0.kt")
|
||||
public void testExtensionComponentFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/extensionComponentFun.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("memberComponentFun.0.kt")
|
||||
public void testMemberComponentFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/components/memberComponentFun.0.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/findUsages/kotlin/findClassUsages")
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.search
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||
@@ -49,12 +50,25 @@ class KotlinReferencesSearchTest(): AbstractSearcherTest() {
|
||||
Assert.assertTrue(refs[2] is KtDestructuringDeclarationReference)
|
||||
}
|
||||
|
||||
fun testComponentFun() {
|
||||
val refs = doTest<KtFunction>()
|
||||
Assert.assertEquals(2, refs.size)
|
||||
Assert.assertEquals("component1", refs[0].canonicalText)
|
||||
Assert.assertTrue(refs[1] is KtDestructuringDeclarationReference)
|
||||
}
|
||||
|
||||
// workaround for KT-9788 AssertionError from backand when we read field from inline function
|
||||
private val myFixtureProxy: JavaCodeInsightTestFixture get() = myFixture
|
||||
|
||||
private inline fun <reified T: PsiElement> doTest(): List<PsiReference> {
|
||||
myFixtureProxy.configureByFile(fileName)
|
||||
val psiFile = myFixtureProxy.configureByFile(fileName)
|
||||
val func = myFixtureProxy.elementAtCaret.getParentOfType<T>(false)!!
|
||||
return ReferencesSearch.search(func).findAll().sortedBy { it.element.textRange.startOffset }
|
||||
val refs = ReferencesSearch.search(func).findAll().sortedBy { it.element.textRange.startOffset }
|
||||
|
||||
// check that local references search gives the same result
|
||||
val localRefs = ReferencesSearch.search(func, LocalSearchScope(psiFile)).findAll()
|
||||
Assert.assertEquals(refs.size, localRefs.size)
|
||||
|
||||
return refs
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user