Initial implementation of faster component function usage search
This commit is contained in:
@@ -16,10 +16,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.psi;
|
package org.jetbrains.kotlin.psi;
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public interface KtWithExpressionInitializer extends PsiElement {
|
public interface KtWithExpressionInitializer extends KtDeclaration {
|
||||||
@Nullable
|
@Nullable
|
||||||
KtExpression getInitializer();
|
KtExpression getInitializer();
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,11 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
|
|
||||||
private val COMPONENT_FUNCTION_NAME_PREFIX = "component"
|
private val COMPONENT_FUNCTION_NAME_PREFIX = "component"
|
||||||
|
|
||||||
fun isComponentLike(name: Name): Boolean {
|
fun isComponentLike(name: Name): Boolean
|
||||||
if (!name.asString().startsWith(COMPONENT_FUNCTION_NAME_PREFIX)) return false
|
= isComponentLike(name.asString())
|
||||||
|
|
||||||
|
fun isComponentLike(name: String): Boolean {
|
||||||
|
if (!name.startsWith(COMPONENT_FUNCTION_NAME_PREFIX)) return false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
getComponentIndex(name)
|
getComponentIndex(name)
|
||||||
@@ -33,8 +36,8 @@ fun isComponentLike(name: Name): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getComponentIndex(componentName: Name): Int =
|
fun getComponentIndex(componentName: String): Int
|
||||||
componentName.asString().substring(COMPONENT_FUNCTION_NAME_PREFIX.length).toInt()
|
= componentName.substring(COMPONENT_FUNCTION_NAME_PREFIX.length).toInt()
|
||||||
|
|
||||||
fun createComponentName(index: Int): Name =
|
fun createComponentName(index: Int): Name
|
||||||
Name.identifier(COMPONENT_FUNCTION_NAME_PREFIX + index)
|
= 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.effectiveSearchScope
|
||||||
import org.jetbrains.kotlin.idea.search.unionSafe
|
import org.jetbrains.kotlin.idea.search.unionSafe
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.dataClassComponentFunction
|
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.getClassNameForCompanionObject
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.getSpecialNamesToSearch
|
import org.jetbrains.kotlin.idea.search.usagesSearch.getSpecialNamesToSearch
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
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.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||||
|
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||||
|
|
||||||
data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean = false,
|
data class KotlinReferencesSearchOptions(val acceptCallableOverrides: Boolean = false,
|
||||||
val acceptOverloads: Boolean = false,
|
val acceptOverloads: Boolean = false,
|
||||||
@@ -96,8 +98,8 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
|||||||
|
|
||||||
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement, filter = refFilter, options = kotlinOptions)
|
val resultProcessor = KotlinRequestResultProcessor(unwrappedElement, filter = refFilter, options = kotlinOptions)
|
||||||
|
|
||||||
|
val name = runReadAction { unwrappedElement.name }
|
||||||
if (kotlinOptions.anyEnabled()) {
|
if (kotlinOptions.anyEnabled()) {
|
||||||
val name = runReadAction { unwrappedElement.name }
|
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
queryParameters.optimizer.searchWord(name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, unwrappedElement,
|
queryParameters.optimizer.searchWord(name, effectiveSearchScope, UsageSearchContext.IN_CODE, true, unwrappedElement,
|
||||||
resultProcessor)
|
resultProcessor)
|
||||||
@@ -114,7 +116,32 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(unwrappedElement is KtElement && isOnlyKotlinSearch(effectiveSearchScope))) {
|
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,
|
private fun searchDataClassComponentUsages(queryParameters: ReferencesSearch.SearchParameters,
|
||||||
containingClass: PsiClass?,
|
containingClass: PsiClass?,
|
||||||
componentFunctionDescriptor: FunctionDescriptor) {
|
componentFunctionDescriptor: FunctionDescriptor,
|
||||||
|
consumer: Processor<PsiReference>
|
||||||
|
) {
|
||||||
val componentFunction = containingClass?.methods?.find {
|
val componentFunction = containingClass?.methods?.find {
|
||||||
it.name == componentFunctionDescriptor.name.asString() && it.parameterList.parametersCount == 0
|
it.name == componentFunctionDescriptor.name.asString() && it.parameterList.parametersCount == 0
|
||||||
}
|
}
|
||||||
if (componentFunction != null) {
|
if (componentFunction != null) {
|
||||||
searchNamedElement(queryParameters, componentFunction)
|
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) {
|
when (element) {
|
||||||
is KtClassOrObject -> processKtClassOrObject(element, queryParameters)
|
is KtClassOrObject -> processKtClassOrObject(element, queryParameters)
|
||||||
is KtNamedFunction, is KtSecondaryConstructor -> {
|
is KtNamedFunction, is KtSecondaryConstructor -> {
|
||||||
@@ -242,14 +272,6 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
|||||||
|
|
||||||
is KtParameter -> {
|
is KtParameter -> {
|
||||||
searchPropertyMethods(queryParameters, element)
|
searchPropertyMethods(queryParameters, element)
|
||||||
runReadAction {
|
|
||||||
|
|
||||||
val componentFunctionDescriptor = element.dataClassComponentFunction()
|
|
||||||
if (componentFunctionDescriptor != null) {
|
|
||||||
val containingClass = element.getStrictParentOfType<KtClassOrObject>()?.toLightClass()
|
|
||||||
searchDataClassComponentUsages(queryParameters, containingClass, componentFunctionDescriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtLightMethod -> {
|
is KtLightMethod -> {
|
||||||
@@ -269,12 +291,6 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
|
|||||||
|
|
||||||
is KtLightParameter -> {
|
is KtLightParameter -> {
|
||||||
val origin = element.kotlinOrigin ?: return
|
val origin = element.kotlinOrigin ?: return
|
||||||
runReadAction {
|
|
||||||
val componentFunctionDescriptor = origin.dataClassComponentFunction()
|
|
||||||
if (componentFunctionDescriptor != null) {
|
|
||||||
searchDataClassComponentUsages(queryParameters, element.method.containingClass, componentFunctionDescriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
searchPropertyMethods(queryParameters, origin)
|
searchPropertyMethods(queryParameters, origin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,14 @@
|
|||||||
package org.jetbrains.kotlin.idea.search.usagesSearch
|
package org.jetbrains.kotlin.idea.search.usagesSearch
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet
|
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.KtToken
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.DelegatedPropertyResolver
|
import org.jetbrains.kotlin.resolve.DelegatedPropertyResolver
|
||||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
|
||||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions.*
|
import org.jetbrains.kotlin.types.expressions.OperatorConventions.*
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
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
|
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]
|
val unaryOp = UNARY_OPERATION_NAMES_WITH_DEPRECATED_INVERTED[this]
|
||||||
if (unaryOp != null) return setOf(unaryOp) to KtSimpleNameReference::class.java
|
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.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
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.idea.search.ideaExtensions.KotlinReferencesSearchOptions
|
||||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
@@ -69,14 +67,7 @@ fun PsiNamedElement.getSpecialNamesToSearch(options: KotlinReferencesSearchOptio
|
|||||||
val name = name
|
val name = name
|
||||||
return when {
|
return when {
|
||||||
name == null || !Name.isValidIdentifier(name) -> Collections.emptyList<String>() to null
|
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 -> {
|
else -> {
|
||||||
val operationSymbolsToSearch = Name.identifier(name).getOperationSymbolsToSearch()
|
val operationSymbolsToSearch = Name.identifier(name).getOperationSymbolsToSearch()
|
||||||
operationSymbolsToSearch.first.map { (it as KtSingleValueToken).value } to operationSymbolsToSearch.second
|
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.asJava.toLightMethods
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
@@ -158,10 +159,10 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
|||||||
if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
|
if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return
|
||||||
if (declaration is KtProperty && declaration.isLocal) return
|
if (declaration is KtProperty && declaration.isLocal) return
|
||||||
if (declaration is KtParameter && (declaration.getParent()?.parent !is KtPrimaryConstructor || !declaration.hasValOrVar())) 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
|
// 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 (isEntryPoint(declaration)) return
|
||||||
if (declaration is KtProperty && declaration.isSerializationImplicitlyUsedField()) return
|
if (declaration is KtProperty && declaration.isSerializationImplicitlyUsedField()) return
|
||||||
if (declaration is KtNamedFunction && declaration.isSerializationImplicitlyUsedMethod()) return
|
if (declaration is KtNamedFunction && declaration.isSerializationImplicitlyUsedMethod()) return
|
||||||
@@ -205,11 +206,6 @@ class UnusedSymbolInspection : AbstractKotlinInspection() {
|
|||||||
return hasTextUsages
|
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 {
|
private fun hasNonTrivialUsages(declaration: KtNamedDeclaration): Boolean {
|
||||||
val psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(declaration.project)
|
val psiSearchHelper = PsiSearchHelper.SERVICE.getInstance(declaration.project)
|
||||||
|
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ class ChangeFunctionReturnTypeFix(element: KtFunction, type: KotlinType) : Kotli
|
|||||||
companion object {
|
companion object {
|
||||||
fun getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(diagnostic: Diagnostic): KtDestructuringDeclarationEntry {
|
fun getDestructuringDeclarationEntryThatTypeMismatchComponentFunction(diagnostic: Diagnostic): KtDestructuringDeclarationEntry {
|
||||||
val componentName = COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.cast(diagnostic).a
|
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")
|
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]
|
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.CallableInfo
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
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.KtDestructuringDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtForExpression
|
||||||
import org.jetbrains.kotlin.resolve.dataClassUtils.getComponentIndex
|
import org.jetbrains.kotlin.resolve.dataClassUtils.getComponentIndex
|
||||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
@@ -40,7 +40,7 @@ object CreateComponentFunctionActionFactory : CreateCallableMemberFromUsageFacto
|
|||||||
val name = diagnosticWithParameters.a
|
val name = diagnosticWithParameters.a
|
||||||
if (!isComponentLike(name)) return null
|
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) }
|
val ownerType = element.initializer?.let { TypeInfo(it, Variance.IN_VARIANCE) }
|
||||||
?: TypeInfo(diagnosticWithParameters.b, 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.psi.search.searches.MethodReferencesSearch
|
||||||
import com.intellij.util.Processor
|
import com.intellij.util.Processor
|
||||||
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
|
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.search.usagesSearch.getOperationSymbolsToSearch
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
||||||
|
|
||||||
class KotlinConventionMethodReferencesSearcher() : QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters>(true) {
|
class KotlinConventionMethodReferencesSearcher() : QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters>(true) {
|
||||||
override fun processQuery(queryParameters: MethodReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
|
override fun processQuery(queryParameters: MethodReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
|
||||||
val method = queryParameters.method
|
val method = queryParameters.method
|
||||||
val name = runReadAction { method.name }
|
val name = runReadAction { method.name }
|
||||||
if (!Name.isValidIdentifier(name)) return
|
if (!Name.isValidIdentifier(name)) return
|
||||||
val operationSymbolsToSearch = Name.identifier(name).getOperationSymbolsToSearch()
|
val identifier = Name.identifier(name)
|
||||||
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 ->
|
if (isComponentLike(identifier)) {
|
||||||
queryParameters.optimizer.searchWord(word, queryParameters.effectiveSearchScope.restrictToKotlinSources(),
|
findDestructuringDeclarationUsages(method, queryParameters.effectiveSearchScope, consumer, queryParameters.optimizer)
|
||||||
UsageSearchContext.IN_CODE, true, method,
|
}
|
||||||
resultProcessor)
|
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
|
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtParameter
|
||||||
// OPTIONS: usages
|
// OPTIONS: usages
|
||||||
|
package pack
|
||||||
|
|
||||||
data class A(val <caret>n: Int, val s: String, val o: Any)
|
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);
|
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")
|
@TestMetadata("contains.0.kt")
|
||||||
public void testContains() throws Exception {
|
public void testContains() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/contains.0.kt");
|
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");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/conventions/unaryMinus.0.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("idea/testData/findUsages/kotlin/findClassUsages")
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.search
|
|||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiReference
|
import com.intellij.psi.PsiReference
|
||||||
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||||
@@ -49,12 +50,25 @@ class KotlinReferencesSearchTest(): AbstractSearcherTest() {
|
|||||||
Assert.assertTrue(refs[2] is KtDestructuringDeclarationReference)
|
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
|
// workaround for KT-9788 AssertionError from backand when we read field from inline function
|
||||||
private val myFixtureProxy: JavaCodeInsightTestFixture get() = myFixture
|
private val myFixtureProxy: JavaCodeInsightTestFixture get() = myFixture
|
||||||
|
|
||||||
private inline fun <reified T: PsiElement> doTest(): List<PsiReference> {
|
private inline fun <reified T: PsiElement> doTest(): List<PsiReference> {
|
||||||
myFixtureProxy.configureByFile(fileName)
|
val psiFile = myFixtureProxy.configureByFile(fileName)
|
||||||
val func = myFixtureProxy.elementAtCaret.getParentOfType<T>(false)!!
|
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