IDEA-228815: Do not use references with recursive calls

GitOrigin-RevId: e4f13ec8bc709f2b543db8122455b3abf9ecb977
This commit is contained in:
Konstantin.Nisht
2020-04-22 18:51:01 +00:00
committed by intellij-monorepo-bot
parent b64d3b665a
commit 54e5e4d2ea
4 changed files with 69 additions and 12 deletions
@@ -1,4 +1,6 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
@file:Suppress("RemoveExplicitTypeArguments")
package org.jetbrains.plugins.groovy.intentions.style.inference.search
import com.intellij.psi.PsiElement
@@ -7,11 +9,16 @@ import com.intellij.psi.search.*
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.parentOfType
import com.intellij.util.Processor
import com.intellij.util.containers.map2Array
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor
import org.jetbrains.plugins.groovy.lang.psi.api.GrFunctionalExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod
@Suppress("RemoveExplicitTypeArguments")
internal fun searchWithClosureAvoidance(element: GrMethod, scope: SearchScope): List<PsiReference> {
val requestsCollector = SearchRequestCollector(SearchSession())
val collector: MutableList<PsiReference> = mutableListOf<PsiReference>()
@@ -21,28 +28,60 @@ internal fun searchWithClosureAvoidance(element: GrMethod, scope: SearchScope):
val query: QuerySearchRequest = queries.removeAt(queries.lastIndex)
if (!query.runQuery()) break
query.collector.takeQueryRequests().forEach { queries.add(it) }
query.collector.takeSearchRequests().forEach { dumpSearchRequest(requestsCollector, it, element) }
query.collector.takeSearchRequests().forEach { requestsCollector.adoptSearchRequest(it, element) }
}
PsiSearchHelper.getInstance(element.project).processRequests(requestsCollector) { ref -> collector.add(ref); true }
return collector
}
private fun dumpSearchRequest(requestsCollector: SearchRequestCollector, request: PsiSearchRequest, target: GrMethod) = with(request) {
requestsCollector.searchWord(word, searchScope, searchContext, caseSensitive, target, ScopeFilteringRequestProcessor(target, processor))
private fun SearchRequestCollector.adoptSearchRequest(request: PsiSearchRequest, target: GrMethod) = with(request) {
searchWord(word, searchScope, searchContext, caseSensitive, target, ScopeFilteringRequestProcessor(target, processor))
}
private class ScopeFilteringRequestProcessor(private val anchorElement: GrMethod,
private val delegateProcessor: RequestResultProcessor) : RequestResultProcessor() {
override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor<in PsiReference>): Boolean {
val enclosingClosure = element.parentOfType<GrFunctionalExpression>()
val call = enclosingClosure?.parentOfType<GrMethodCall>()
val arguments = call?.closureArguments?.asList()?.plus(call.expressionArguments.asList())
if (arguments?.contains(enclosingClosure) == true && call.callReference?.methodName == anchorElement.name) {
return true
private class CollisionFinder(vararg val bannedIdentifiers: String) : GroovyRecursiveElementVisitor() {
var foundCollision = false
override fun visitElement(element: GroovyPsiElement) {
if (!foundCollision) super.visitElement(element)
}
else {
return delegateProcessor.processTextOccurrence(element, offsetInElement, consumer)
override fun visitReferenceExpression(referenceExpression: GrReferenceExpression) {
if (referenceExpression.referenceName in bannedIdentifiers) {
foundCollision = true
}
else {
super.visitReferenceExpression(referenceExpression)
}
}
override fun visitFunctionalExpression(expression: GrFunctionalExpression) {
// it is sufficient for references searcher to know that argument has closure type, no need to go deeper
}
}
override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor<in PsiReference>): Boolean {
val enclosingClosure: GrFunctionalExpression? = element.parentOfType<GrFunctionalExpression>()
val call: GrMethodCall? = enclosingClosure?.parentOfType<GrMethodCall>()
val arguments: List<GrExpression>? = call?.closureArguments?.asList()?.plus(call.expressionArguments.asList())
if (arguments?.contains(enclosingClosure) == true && call.callReference?.methodName == anchorElement.name) {
return true
}
val enclosingCall: GrMethodCall? = element.parentOfType<GrMethodCall>()?.takeIf { it.invokedExpression === element }
if (enclosingCall is GrMethodCall) {
val enclosingMethod: GrMethod? = enclosingCall.parentOfType<GrMethod>()?.takeIf { it === anchorElement }
val bannedIdentifiers: Array<String> = enclosingMethod?.parameters?.map2Array { it.name } ?: emptyArray()
val collisionFinder = CollisionFinder(anchorElement.name, *bannedIdentifiers)
if (enclosingCall.expressionArguments.any { argument -> collisionFinder.apply { argument.accept(this) }.foundCollision }) {
// there should be provided some connection between method return type and its argument,
// but currently this option is absent
return true
}
}
return delegateProcessor.processTextOccurrence(element, offsetInElement, consumer)
}
}
@@ -560,4 +560,8 @@ class AA { static void fo<caret>o(Integer a) {} }
void testForInLoopWithMap() {
doTest(true)
}
void testRecursiveCalls() {
doTest(true)
}
}
@@ -0,0 +1,7 @@
def foo(x) {
x
}
def bar() {
foo(foo(1))
}
@@ -0,0 +1,7 @@
Integer foo(Integer x) {
x
}
def bar() {
foo(foo(1))
}