Down-shift to plain search if reference in unknown language encountered

This commit is contained in:
Valentin Kipyatkov
2016-09-05 17:40:28 +03:00
parent e314e22215
commit 5b638f51b0
26 changed files with 143 additions and 127 deletions
@@ -16,6 +16,8 @@
package org.jetbrains.kotlin.idea.search
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.fileTypes.FileTypeRegistry
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
@@ -65,6 +67,23 @@ fun SearchScope.restrictToKotlinSources(): SearchScope {
}
}
fun SearchScope.excludeKotlinSources(): SearchScope = excludeFileTypes(KotlinFileType.INSTANCE)
fun SearchScope.excludeFileTypes(vararg fileTypes: FileType): SearchScope {
if (this is GlobalSearchScope) {
val includedFileTypes = FileTypeRegistry.getInstance().registeredFileTypes.filter { it !in fileTypes }.toTypedArray()
return GlobalSearchScope.getScopeRestrictedByFileTypes(this, *includedFileTypes)
}
else {
this as LocalSearchScope
val filteredElements = scope.filter { it.containingFile.fileType !in fileTypes }
return if (filteredElements.isNotEmpty())
LocalSearchScope(filteredElements.toTypedArray())
else
GlobalSearchScope.EMPTY_SCOPE
}
}
// Copied from SearchParameters.getEffectiveSearchScope()
fun ReferencesSearch.SearchParameters.effectiveSearchScope(element: PsiElement): SearchScope {
if (element == elementToSearch) return effectiveSearchScope
@@ -17,8 +17,8 @@
package org.jetbrains.kotlin.idea.search.usagesSearch
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.ide.highlighter.XmlFileType
import com.intellij.lang.java.JavaLanguage
import com.intellij.lang.xml.XMLLanguage
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootManager
@@ -36,12 +36,14 @@ import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.idea.KotlinFileType
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.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.search.excludeFileTypes
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
@@ -179,7 +181,8 @@ class ExpressionsOfTypeProcessor(
data class ProcessClassUsagesTask(val classToSearch: PsiClass) : Task {
override fun perform() {
testLog?.add("Searched references to ${logPresentation(classToSearch)}")
searchReferences(classToSearch, GlobalSearchScope.allScope(project)) { reference ->
val scope = GlobalSearchScope.allScope(project).excludeFileTypes(XmlFileType.INSTANCE) // ignore usages in XML - they don't affect us
searchReferences(classToSearch, scope) { reference ->
if (processClassUsage(reference)) return@searchReferences true
if (mode != Mode.ALWAYS_SMART) {
@@ -200,21 +203,26 @@ class ExpressionsOfTypeProcessor(
addTask(ProcessClassUsagesTask(classToSearch))
}
private fun addCallableDeclarationToProcess(declaration: PsiElement, processMethod: (PsiReference) -> Unit) {
private fun addCallableDeclarationToProcess(declaration: PsiElement, processMethod: (PsiReference) -> Boolean) {
if (declaration.isOperatorExpensiveToSearch()) { // cancel all tasks and use plain search
downShiftToPlainSearch()
return
}
data class ProcessCallableUsagesTask(val declaration: PsiElement, val processMethod: (PsiReference) -> Unit) : Task {
data class ProcessCallableUsagesTask(val declaration: PsiElement, val processMethod: (PsiReference) -> Boolean) : Task {
override fun perform() {
// 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 scope = GlobalSearchScope.projectScope(project).restrictToKotlinSources()
testLog?.add("Searched references to ${logPresentation(declaration)} in Kotlin files")
val scope = GlobalSearchScope.projectScope(project).excludeFileTypes(JavaFileType.INSTANCE, XmlFileType.INSTANCE)
testLog?.add("Searched references to ${logPresentation(declaration)} in non-Java files")
val searchParameters = KotlinReferencesSearchParameters(
declaration, scope, kotlinOptions = KotlinReferencesSearchOptions(searchNamedArguments = false))
searchReferences(searchParameters) { reference -> processMethod(reference); true }
searchReferences(searchParameters) { reference ->
val processed = processMethod(reference)
if (!processed) { // we don't know how to handle this reference and down-shift to plain search
downShiftToPlainSearch()
}
processed
}
}
}
addTask(ProcessCallableUsagesTask(declaration, processMethod))
@@ -226,30 +234,42 @@ class ExpressionsOfTypeProcessor(
/**
* Process reference to declaration whose type is our class (or our class used anywhere inside that type)
*/
private fun processReferenceToCallableOfOurType(reference: PsiReference) {
if (reference is KtDestructuringDeclarationReference) {
// declaration usage in form of destructuring declaration entry
addCallableDeclarationToProcess(reference.element, HAS_OUR_TYPE)
}
else {
(reference.element as? KtReferenceExpression)?.let { processSuspiciousExpression(it) }
private fun processReferenceToCallableOfOurType(reference: PsiReference): Boolean {
when (reference.element.language) {
KotlinLanguage.INSTANCE -> {
if (reference is KtDestructuringDeclarationReference) {
// declaration usage in form of destructuring declaration entry
addCallableDeclarationToProcess(reference.element, HAS_OUR_TYPE)
}
else {
(reference.element as? KtReferenceExpression)?.let { processSuspiciousExpression(it) }
}
return true
}
else -> return false // reference in unknown language - we don't know how to handle it
}
}
/**
* Process reference to declaration which has parameter of functional type with our class used inside
*/
private fun processLambdasByCallableReference(reference: PsiReference) {
private fun processLambdasByCallableReference(reference: PsiReference): Boolean {
(reference.element as? KtReferenceExpression)?.let { processLambdasForCallableReference(it) }
return true
}
private fun addSamInterfaceToProcess(psiClass: PsiClass) {
data class ProcessSamInterfaceTask(val psiClass: PsiClass) : Task {
override fun perform() {
//TODO: what about other JVM languages?
val scope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(project), JavaFileType.INSTANCE)
testLog?.add("Searched references to ${logPresentation(psiClass)} in java files")
val scope = GlobalSearchScope.projectScope(project).excludeFileTypes(KotlinFileType.INSTANCE, XmlFileType.INSTANCE)
testLog?.add("Searched references to ${logPresentation(psiClass)} in non-Kotlin files")
searchReferences(psiClass, scope) { reference ->
if (reference.element.language != JavaFileType.INSTANCE) { // reference in some JVM language can be method parameter (but we don't know)
downShiftToPlainSearch()
return@searchReferences false
}
// 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
@@ -273,8 +293,6 @@ class ExpressionsOfTypeProcessor(
JavaLanguage.INSTANCE -> processClassUsageInJava(element)
XMLLanguage.INSTANCE -> true // ignore usages in XML - they don't affect us
else -> false // we don't know anything about usages in other languages - so we downgrade to slow algorithm in this case
}
}