Commit 70379901 authored by Daniil Ovchinnikov's avatar Daniil Ovchinnikov
Browse files

[groovy] resolve r-value references separately from method calls

parent 53b07cf9
Branches unavailable Tags unavailable
No related merge requests found
Showing with 221 additions and 9 deletions
+221 -9
// Copyright 2000-2018 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.
package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementTypes
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult
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.impl.statements.expressions.GrSuperReferenceResolver.resolveSuperExpression
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrThisReferenceResolver.resolveThisExpression
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil
import org.jetbrains.plugins.groovy.lang.resolve.GrReferenceResolveRunner
import org.jetbrains.plugins.groovy.lang.resolve.api.GroovyCachingReference
import org.jetbrains.plugins.groovy.lang.resolve.processors.GroovyRValueProcessor
import org.jetbrains.plugins.groovy.lang.resolve.processors.GroovyResolveKind.*
import java.util.*
abstract class GrReferenceExpressionReference(ref: GrReferenceExpressionImpl) : GroovyCachingReference<GrReferenceExpressionImpl>(ref) {
......@@ -20,7 +30,24 @@ abstract class GrReferenceExpressionReference(ref: GrReferenceExpressionImpl) :
class GrRValueExpressionReference(ref: GrReferenceExpressionImpl) : GrReferenceExpressionReference(ref) {
override fun doResolveNonStatic(incomplete: Boolean): Collection<GroovyResolveResult> {
return element.doPolyResolve(incomplete, true)
val expression = element
if (expression.parent is GrMethodCall || incomplete) {
return expression.doPolyResolve(incomplete, true)
}
expression.handleSpecialCases()?.let {
return it
}
val name = expression.referenceName ?: return emptyList()
val kinds = if (expression.isQualified) {
EnumSet.of(FIELD, PROPERTY, VARIABLE)
}
else {
EnumSet.of(FIELD, PROPERTY, VARIABLE, BINDING)
}
val processor = GroovyRValueProcessor(name, expression, kinds)
GrReferenceResolveRunner(expression, processor).resolveReferenceExpression()
return processor.results
}
}
......@@ -30,3 +57,16 @@ class GrLValueExpressionReference(ref: GrReferenceExpressionImpl) : GrReferenceE
return element.doPolyResolve(incomplete, false)
}
}
private fun GrReferenceExpression.handleSpecialCases(): Collection<GroovyResolveResult>? {
when (referenceNameElement?.node?.elementType) {
GroovyElementTypes.KW_THIS -> return resolveThisExpression(this)
GroovyElementTypes.KW_SUPER -> return resolveSuperExpression(this)
GroovyElementTypes.KW_CLASS -> {
if (!PsiUtil.isCompileStatic(this) && qualifier?.type == null) {
return emptyList()
}
}
}
return null
}
......@@ -47,3 +47,5 @@ fun GrExpression?.isThisExpression(): Boolean {
fun GrOperatorExpression.multiResolve(): Array<out GroovyResolveResult> {
return reference?.multiResolve(false) ?: GroovyResolveResult.EMPTY_ARRAY
}
fun elementInfo(element: PsiElement): String = "Element: $element; class: ${element.javaClass}; text: ${element.text}"
......@@ -21,11 +21,12 @@ import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.DefaultConstructor
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrBindingVariable
import org.jetbrains.plugins.groovy.lang.psi.util.skipSameTypeParents
import org.jetbrains.plugins.groovy.lang.resolve.api.GroovyProperty
import org.jetbrains.plugins.groovy.lang.resolve.imports.importedNameKey
import org.jetbrains.plugins.groovy.lang.resolve.processors.DynamicMembersHint
import org.jetbrains.plugins.groovy.lang.resolve.processors.GroovyResolveKind
import org.jetbrains.plugins.groovy.lang.resolve.processors.GroovyResolverProcessor
val log: Logger = logger(::log)
......@@ -74,9 +75,9 @@ fun PsiScopeProcessor.shouldProcessLocals(): Boolean = shouldProcess(GroovyResol
fun PsiScopeProcessor.shouldProcessFields(): Boolean = shouldProcess(GroovyResolveKind.FIELD)
fun PsiScopeProcessor.shouldProcessMethods(): Boolean {
return ResolveUtil.shouldProcessMethods(getHint(ElementClassHint.KEY))
}
fun PsiScopeProcessor.shouldProcessMethods(): Boolean = shouldProcess(GroovyResolveKind.METHOD)
fun PsiScopeProcessor.shouldProcessProperties(): Boolean = shouldProcess(GroovyResolveKind.PROPERTY)
fun PsiScopeProcessor.shouldProcessClasses(): Boolean {
return ResolveUtil.shouldProcessClasses(getHint(ElementClassHint.KEY))
......@@ -95,10 +96,6 @@ fun PsiScopeProcessor.shouldProcessTypeParameters(): Boolean {
return groovyKindHint.shouldProcess(GroovyResolveKind.TYPE_PARAMETER)
}
fun PsiScopeProcessor.shouldProcessProperties(): Boolean {
return this is GroovyResolverProcessor && isPropertyResolve
}
private fun PsiScopeProcessor.shouldProcess(kind: GroovyResolveKind): Boolean {
val resolveKindHint = getHint(GroovyResolveKind.HINT_KEY)
if (resolveKindHint != null) return resolveKindHint.shouldProcess(kind)
......@@ -159,3 +156,16 @@ fun valid(allCandidates: Collection<GroovyResolveResult>): List<GroovyResolveRes
fun singleOrValid(allCandidates: List<GroovyResolveResult>): List<GroovyResolveResult> {
return if (allCandidates.size <= 1) allCandidates else valid(allCandidates)
}
fun getResolveKind(element: PsiNamedElement): GroovyResolveKind? {
return when (element) {
is PsiClass -> GroovyResolveKind.CLASS
is PsiPackage -> GroovyResolveKind.PACKAGE
is PsiMethod -> GroovyResolveKind.METHOD
is PsiField -> GroovyResolveKind.FIELD
is GrBindingVariable -> GroovyResolveKind.BINDING
is PsiVariable -> GroovyResolveKind.VARIABLE
is GroovyProperty -> GroovyResolveKind.PROPERTY
else -> null
}
}
// Copyright 2000-2018 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.
package org.jetbrains.plugins.groovy.lang.resolve.processors
import com.intellij.psi.PsiElement
import com.intellij.psi.scope.ElementClassHint
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult
import org.jetbrains.plugins.groovy.lang.resolve.GrResolverProcessor
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil.filterSameSignatureCandidates
import org.jetbrains.plugins.groovy.lang.resolve.singleOrValid
abstract class AccessorAwareResolverProcessor(
name: String,
place: PsiElement,
kinds: Set<GroovyResolveKind>
) : KindsResolverProcessor(name, place, kinds),
GrResolverProcessor<GroovyResolveResult>,
ElementClassHint,
DynamicMembersHint,
MultiProcessor {
init {
@Suppress("LeakingThis") hint(ElementClassHint.KEY, this)
@Suppress("LeakingThis") hint(DynamicMembersHint.KEY, this)
}
final override fun shouldProcessProperties(): Boolean = true
final override fun shouldProcessMethods(): Boolean = false
final override fun shouldProcess(kind: ElementClassHint.DeclarationKind): Boolean {
return kind != ElementClassHint.DeclarationKind.METHOD && kinds.any { kind in it.declarationKinds }
}
final override fun getProcessors(): Collection<PsiScopeProcessor> = listOf(this) + accessorProcessors
protected abstract val accessorProcessors: Collection<GrResolverProcessor<*>>
private val accessorCandidates get() = accessorProcessors.flatMap { it.results }
private fun getCandidates(kind: GroovyResolveKind): List<GroovyResolveResult> {
val result = getCandidate(kind)?.let(::listOf) ?: emptyList()
return if (kind == GroovyResolveKind.PROPERTY) result + accessorCandidates else result
}
final override val results: List<GroovyResolveResult>
get() {
val variables = getCandidates(GroovyResolveKind.VARIABLE)
if (variables.isNotEmpty()) {
return variables
}
val properties = singleOrValid(getCandidates(GroovyResolveKind.PROPERTY))
if (!properties.isEmpty()) {
return if (properties.size <= 1) properties else ContainerUtil.newSmartList(properties[0])
}
val fields = getCandidates(GroovyResolveKind.FIELD)
if (!fields.isEmpty()) {
return fields
}
if (properties.isNotEmpty()) {
return properties
}
val bindings = getCandidates(GroovyResolveKind.BINDING)
if (bindings.isNotEmpty()) {
return bindings
}
// TODO this is used to choose between two same methods from some class and its superclass, which is questionable
return getAllCandidates() + filterSameSignatureCandidates(accessorCandidates)
}
}
// Copyright 2000-2018 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.
package org.jetbrains.plugins.groovy.lang.resolve.processors
import com.intellij.lang.java.beans.PropertyKind
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiType
import org.jetbrains.plugins.groovy.lang.psi.util.isPropertyName
import org.jetbrains.plugins.groovy.lang.resolve.GrResolverProcessor
class GroovyRValueProcessor(
name: String,
place: PsiElement,
kinds: Set<GroovyResolveKind>
) : AccessorAwareResolverProcessor(name, place, kinds) {
override val accessorProcessors: Collection<GrResolverProcessor<*>> = if (name.isPropertyName())
listOf(
AccessorProcessor(name, PropertyKind.GETTER, { PsiType.EMPTY_ARRAY }, place),
AccessorProcessor(name, PropertyKind.BOOLEAN_GETTER, { PsiType.EMPTY_ARRAY }, place)
)
else {
emptyList()
}
}
// Copyright 2000-2018 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.
package org.jetbrains.plugins.groovy.lang.resolve.processors
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.ResolveState
import com.intellij.psi.scope.NameHint
import com.intellij.psi.scope.ProcessorWithHints
import com.intellij.util.enumMapOf
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult
import org.jetbrains.plugins.groovy.lang.psi.util.elementInfo
import org.jetbrains.plugins.groovy.lang.resolve.BaseGroovyResolveResult
import org.jetbrains.plugins.groovy.lang.resolve.getName
import org.jetbrains.plugins.groovy.lang.resolve.getResolveKind
open class KindsResolverProcessor(
protected val name: String,
protected val place: PsiElement,
protected val kinds: Set<GroovyResolveKind>
) : ProcessorWithHints(),
NameHint,
GroovyResolveKind.Hint {
init {
@Suppress("LeakingThis") hint(NameHint.KEY, this)
@Suppress("LeakingThis") hint(GroovyResolveKind.HINT_KEY, this)
}
final override fun getName(state: ResolveState): String? = name
final override fun shouldProcess(kind: GroovyResolveKind): Boolean = kind in kinds
private val candidates = enumMapOf<GroovyResolveKind, GroovyResolveResult>()
final override fun execute(element: PsiElement, state: ResolveState): Boolean {
if (element !is PsiNamedElement) return true
require(element.isValid) {
"Invalid element. ${elementInfo(element)}"
}
val elementName = getName(state, element)
if (name != elementName) return true
val kind = getResolveKind(element) ?: return true
if (kind !in kinds) {
return true
}
if (kind in candidates) {
return true
}
candidates[kind] = BaseGroovyResolveResult(element, place, state)
return true
}
fun getCandidate(kind: GroovyResolveKind): GroovyResolveResult? = candidates[kind]
fun getAllCandidates(): List<GroovyResolveResult> = candidates.values.toList()
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment