[groovy] resolve method references with top level type as receiver

Support top-level type for reference expressions and index property expressions.
This fixes cycle when operator resolution wanted receiver type and receiver type required inference from context,
i.e. from resolved operator.

GitOrigin-RevId: 2c2818e2b5689ddf0c65aac2d87b76d11f1f2ebc
This commit is contained in:
Daniil Ovchinnikov
2019-11-07 02:40:58 +00:00
committed by intellij-monorepo-bot
parent dccdf09d73
commit 28fcfceaaf
3 changed files with 51 additions and 13 deletions
@@ -11,6 +11,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState
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.expressions.path.GrIndexProperty
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil.getQualifierType
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil
import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrTypeConverter
@@ -24,22 +25,23 @@ import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint
import org.jetbrains.plugins.groovy.lang.typing.devoid
fun getTopLevelType(expression: GrExpression): PsiType? {
if (expression is GrMethodCall) {
val resolved = expression.advancedResolve() as? GroovyMethodResult
resolved?.candidate?.let {
val session = GroovyInferenceSessionBuilder(expression, it, resolved.contextSubstitutor)
.resolveMode(false)
.build()
return session.inferSubst().substitute(PsiUtil.getSmartReturnType(it.method).devoid(expression))
}
return null
}
if (expression is GrFunctionalExpression) {
return TypesUtil.createTypeByFQClassName(GroovyCommonClassNames.GROOVY_LANG_CLOSURE, expression)
}
return expression.type
val result = when (expression) {
is GrMethodCall -> expression.advancedResolve() as? GroovyMethodResult
is GrReferenceExpression -> expression.rValueReference?.advancedResolve() as? GroovyMethodResult ?: return expression.type
is GrIndexProperty -> expression.rValueReference?.advancedResolve() as? GroovyMethodResult ?: return expression.type
else -> return expression.type
}
return result?.candidate?.let {
val session = GroovyInferenceSessionBuilder(expression, it, result.contextSubstitutor)
.resolveMode(false)
.build()
session.inferSubst().substitute(PsiUtil.getSmartReturnType(it.method).devoid(expression))
}
}
fun buildQualifier(ref: GrReferenceExpression?, state: ResolveState): Argument {
@@ -18,7 +18,7 @@ import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint
import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint.STATIC_CONTEXT
fun Argument.processReceiver(processor: PsiScopeProcessor, state: ResolveState, place: PsiElement): Boolean {
val receiverType: PsiType = type ?: TypesUtil.getJavaLangObject(place) ?: return true
val receiverType: PsiType = topLevelType ?: TypesUtil.getJavaLangObject(place) ?: return true
return receiverType.doProcessReceiverType0(processor, state.put(ClassHint.RECEIVER, this), place)
}
@@ -9,6 +9,7 @@ import org.jetbrains.plugins.groovy.GroovyProjectDescriptors
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod
import org.jetbrains.plugins.groovy.util.LightProjectTest
import org.jetbrains.plugins.groovy.util.ResolveTest
@@ -49,6 +50,9 @@ class IdCallable {
class GenericPropertyContainer {
def <T> I<T> getGenericProperty() {}
def <T> void setGenericProperty(I<T> c) {}
def <T> List<T> getGenericList() {}
def <T> void setGenericList(List<T> l) {}
}
class Files {
@@ -242,6 +246,38 @@ static <T> T ppp(Producer<T> p) {}
assertSubstitutor(op.reference.advancedResolve(), 'java.io.File')
}
@Test
void 'plus assignment generic property r-value'() {
def ref = elementUnderCaret('new GenericPropertyContainer().<caret>genericList += new ArrayList<String>()', GrReferenceExpression)
assertSubstitutor(ref.RValueReference.advancedResolve(), JAVA_LANG_STRING)
}
@Test
void 'plus assignment generic property'() {
def op = elementUnderCaret('new GenericPropertyContainer().genericList <caret>+= new ArrayList<String>()', GrAssignmentExpression)
assertSubstitutor(op.reference.advancedResolve(), JAVA_LANG_STRING)
}
@Ignore("we don't yet infer l-value substitutors")
@Test
void 'plus assignment generic property l-value'() {
def ref = elementUnderCaret('new GenericPropertyContainer().<caret>genericList += new ArrayList<String>()', GrReferenceExpression)
assertSubstitutor(ref.LValueReference.advancedResolve(), JAVA_LANG_STRING)
}
@Test
void 'plus assignment with index r-value'() {
def op = elementUnderCaret('Map<Number, String> mns; mns<caret>[42] += "foo"', GrIndexProperty)
assertSubstitutor(op.RValueReference.advancedResolve(), JAVA_LANG_NUMBER, JAVA_LANG_STRING)
}
@Ignore("we don't yet infer l-value substitutors")
@Test
void 'plus assignment with index l-value'() {
def op = elementUnderCaret('Map<Number, String> mns; mns<caret>[42] += "foo"', GrIndexProperty)
assertSubstitutor(op.LValueReference.advancedResolve(), JAVA_LANG_NUMBER, JAVA_LANG_STRING)
}
@Test
void 'same method nested'() {
def call = elementUnderCaret '''\