SAM-adapters supported
This commit is contained in:
+95
-34
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.search.usagesSearch
|
package org.jetbrains.kotlin.idea.search.usagesSearch
|
||||||
|
|
||||||
|
import com.intellij.ide.highlighter.JavaFileType
|
||||||
import com.intellij.lang.java.JavaLanguage
|
import com.intellij.lang.java.JavaLanguage
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
@@ -33,6 +34,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
@@ -45,6 +47,7 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
|||||||
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
|
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
|
||||||
import org.jetbrains.kotlin.idea.util.toFuzzyType
|
import org.jetbrains.kotlin.idea.util.toFuzzyType
|
||||||
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
|
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
|
||||||
|
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
import org.jetbrains.kotlin.resolve.dataClassUtils.getComponentIndex
|
import org.jetbrains.kotlin.resolve.dataClassUtils.getComponentIndex
|
||||||
@@ -129,8 +132,14 @@ private class Processor(
|
|||||||
//TODO: what about Scala and other JVM-languages?
|
//TODO: what about Scala and other JVM-languages?
|
||||||
private val declarationUsageScope = GlobalSearchScope.projectScope(project).restrictToKotlinSources()
|
private val declarationUsageScope = GlobalSearchScope.projectScope(project).restrictToKotlinSources()
|
||||||
|
|
||||||
private val declarationsToProcess = ArrayDeque<PsiElement>()
|
// note: a Task must define equals & hashCode!
|
||||||
private val declarationsToProcessSet = HashSet<PsiElement>()
|
private interface Task {
|
||||||
|
fun perform()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val tasks = ArrayDeque<Task>()
|
||||||
|
private val taskSet = HashSet<Any>()
|
||||||
|
|
||||||
private val scopesToUsePlainSearch = LinkedHashMap<KtFile, ArrayList<PsiElement>>()
|
private val scopesToUsePlainSearch = LinkedHashMap<KtFile, ArrayList<PsiElement>>()
|
||||||
|
|
||||||
fun run() {
|
fun run() {
|
||||||
@@ -170,21 +179,71 @@ private class Processor(
|
|||||||
(classToSearch as? KtLightClass)?.kotlinOrigin?.let { usePlainSearch(it) }
|
(classToSearch as? KtLightClass)?.kotlinOrigin?.let { usePlainSearch(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
processDeclarations()
|
processTasks()
|
||||||
|
|
||||||
val scopeElements = scopesToUsePlainSearch.values.flatMap { it }.toTypedArray()
|
val scopeElements = scopesToUsePlainSearch.values.flatMap { it }.toTypedArray()
|
||||||
plainSearchHandler(LocalSearchScope(scopeElements))
|
plainSearchHandler(LocalSearchScope(scopeElements))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun addTask(task: Task) {
|
||||||
|
if (taskSet.add(task)) {
|
||||||
|
tasks.push(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processTasks() {
|
||||||
|
while (tasks.isNotEmpty()) {
|
||||||
|
tasks.pop().perform()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: check if it's operator (too expensive)
|
//TODO: check if it's operator (too expensive)
|
||||||
/**
|
/**
|
||||||
* Adds declaration whose type is our data class (or data class used anywhere inside that type)
|
* Adds declaration whose type is our data class (or data class used anywhere inside that type)
|
||||||
* or which has parameter of functional type with our data class used inside
|
* or which has parameter of functional type with our data class used inside
|
||||||
*/
|
*/
|
||||||
private fun addCallableDeclarationToProcess(declaration: PsiElement) {
|
private fun addCallableDeclarationToProcess(declaration: PsiElement) {
|
||||||
if (declarationsToProcessSet.add(declaration)) {
|
data class ProcessCallableUsagesTask(val declaration: PsiElement) : Task {
|
||||||
declarationsToProcess.push(declaration)
|
override fun perform() {
|
||||||
|
ReferencesSearch.search(declaration, declarationUsageScope).forEach { reference ->
|
||||||
|
if (reference is KtDestructuringDeclarationReference) { // declaration usage in form of destructuring declaration
|
||||||
|
val entries = reference.element.entries
|
||||||
|
val componentIndex = when (declaration) {
|
||||||
|
is KtParameter -> declaration.dataClassComponentFunction()?.name?.asString()?.let { getComponentIndex(it) }
|
||||||
|
is KtFunction -> declaration.name?.let { getComponentIndex(it) }
|
||||||
|
//TODO: java component functions (see KT-13605)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
if (componentIndex != null && componentIndex <= entries.size) {
|
||||||
|
addCallableDeclarationToProcess(entries[componentIndex - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(reference.element as? KtReferenceExpression)?.let { processSuspiciousExpression(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
addTask(ProcessCallableUsagesTask(declaration))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addSamInterfaceToProcess(psiClass: PsiClass) {
|
||||||
|
data class SamInterfaceTask(val psiClass: PsiClass) : Task {
|
||||||
|
override fun perform() {
|
||||||
|
//TODO: what about other JVM languages?
|
||||||
|
val scope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(project), JavaFileType.INSTANCE)
|
||||||
|
ReferencesSearch.search(psiClass, scope).forEach { reference ->
|
||||||
|
// check if the reference is method parameter type
|
||||||
|
val parameter = ((reference as? PsiJavaCodeReferenceElement)?.parent as? PsiTypeElement)?.parent as? PsiParameter
|
||||||
|
val method = parameter?.declarationScope as? PsiMethod
|
||||||
|
if (method != null) {
|
||||||
|
addCallableDeclarationToProcess(method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addTask(SamInterfaceTask(psiClass))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -305,51 +364,53 @@ private class Processor(
|
|||||||
private fun processJavaDataClassUsage(element: PsiElement): Boolean {
|
private fun processJavaDataClassUsage(element: PsiElement): Boolean {
|
||||||
if (element !is PsiJavaCodeReferenceElement) return true // meaningless reference from Java
|
if (element !is PsiJavaCodeReferenceElement) return true // meaningless reference from Java
|
||||||
|
|
||||||
|
var prev = element
|
||||||
ParentsLoop@
|
ParentsLoop@
|
||||||
for (parent in element.parents) {
|
for (parent in element.parents) {
|
||||||
when (parent) {
|
when (parent) {
|
||||||
is PsiCodeBlock,
|
is PsiCodeBlock,
|
||||||
is PsiExpression,
|
is PsiExpression,
|
||||||
is PsiImportStatement,
|
is PsiImportStatement ->
|
||||||
is PsiParameter -> //TODO: if Java parameter has Kotlin functional type then we should process method usages
|
break@ParentsLoop // ignore local usages and usages in imports
|
||||||
break@ParentsLoop // ignore local usages, usages in imports and in parameter types
|
|
||||||
|
|
||||||
is PsiMethod, is PsiField -> {
|
//TODO: if Java parameter has Kotlin functional type then we should process method usages
|
||||||
if (!(parent as PsiModifierListOwner).isPrivateOrLocal()) {
|
is PsiParameter -> {
|
||||||
|
if (prev == parent.typeElement) {
|
||||||
|
val method = parent.declarationScope as? PsiMethod
|
||||||
|
if (method != null && method.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||||
|
val psiClass = method.containingClass
|
||||||
|
if (psiClass != null) {
|
||||||
|
val classDescriptor = psiClass.resolveToDescriptor(target.getResolutionFacade())
|
||||||
|
if (classDescriptor != null && SingleAbstractMethodUtils.getSingleAbstractMethodOrNull(classDescriptor) != null) {
|
||||||
|
addSamInterfaceToProcess(psiClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break@ParentsLoop
|
||||||
|
}
|
||||||
|
|
||||||
|
is PsiMethod -> {
|
||||||
|
if (prev == parent.returnTypeElement && !parent.isPrivateOrLocal()) {
|
||||||
|
addCallableDeclarationToProcess(parent)
|
||||||
|
}
|
||||||
|
break@ParentsLoop
|
||||||
|
}
|
||||||
|
|
||||||
|
is PsiField -> {
|
||||||
|
if (prev == parent.typeElement && !parent.isPrivateOrLocal()) {
|
||||||
addCallableDeclarationToProcess(parent)
|
addCallableDeclarationToProcess(parent)
|
||||||
}
|
}
|
||||||
break@ParentsLoop
|
break@ParentsLoop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prev = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processDeclarations() {
|
|
||||||
while (declarationsToProcess.isNotEmpty()) {
|
|
||||||
val declaration = declarationsToProcess.pop()
|
|
||||||
|
|
||||||
ReferencesSearch.search(declaration, declarationUsageScope).forEach { reference ->
|
|
||||||
if (reference is KtDestructuringDeclarationReference) { // declaration usage in form of destructuring declaration
|
|
||||||
val entries = reference.element.entries
|
|
||||||
val componentIndex = when (declaration) {
|
|
||||||
is KtParameter -> declaration.dataClassComponentFunction()?.name?.asString()?.let { getComponentIndex(it) }
|
|
||||||
is KtFunction -> declaration.name?.let { getComponentIndex(it) }
|
|
||||||
//TODO: java component functions (see KT-13605)
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
if (componentIndex != null && componentIndex <= entries.size) {
|
|
||||||
addCallableDeclarationToProcess(entries[componentIndex - 1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
(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)
|
* Process expression which may have type of our data class (or data class used anywhere inside that type)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import java.util.List;
|
import kotlin.Unit;
|
||||||
import pack.A;
|
import pack.A;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
class JavaClass {
|
class JavaClass {
|
||||||
public List<A> getA() {
|
public List<A> getA() {
|
||||||
A a = new A(1, "", "");
|
A a = new A(1, "", "");
|
||||||
@@ -8,4 +11,10 @@ class JavaClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void takeA(A a){}
|
public void takeA(A a){}
|
||||||
|
|
||||||
|
public void takeSAM(JavaSAM sam){}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface JavaSAM {
|
||||||
|
void takeA(A a);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import pack.A
|
||||||
|
|
||||||
|
fun foo(javaClass: JavaClass) {
|
||||||
|
javaClass.takeSAM { val (x, y) = it }
|
||||||
|
}
|
||||||
+2
-1
@@ -23,4 +23,5 @@
|
|||||||
[dataClass.4.kt] Value read 27 takeFun2 { a, n -> val (x, y) = a!! }
|
[dataClass.4.kt] Value read 27 takeFun2 { a, n -> val (x, y) = a!! }
|
||||||
[dataClass.4.kt] Value read 28 takeFun3 { val (x, y) = it[0] }
|
[dataClass.4.kt] Value read 28 takeFun3 { val (x, y) = it[0] }
|
||||||
[dataClass.4.kt] Value read 30 x(p) { val (x, y) = it }
|
[dataClass.4.kt] Value read 30 x(p) { val (x, y) = it }
|
||||||
[dataClass.5.kt] Value read 10 "".v = { val (x, y ) = it }
|
[dataClass.5.kt] Value read 10 "".v = { val (x, y ) = it }
|
||||||
|
[dataClass.6.kt] Value read 4 javaClass.takeSAM { val (x, y) = it }
|
||||||
|
|||||||
Reference in New Issue
Block a user