gradle: honor closure delegatesTo type in case of methods name conflicts

This commit is contained in:
Vladislav.Soroka
2016-12-06 15:15:06 +03:00
parent 18cf72ea99
commit b6ca56b3e4
4 changed files with 92 additions and 18 deletions
@@ -46,6 +46,8 @@ import org.jetbrains.plugins.groovy.lang.psi.patterns.groovyClosure
import org.jetbrains.plugins.groovy.lang.psi.patterns.psiMethod
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames.GROOVY_LANG_CLOSURE
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil
import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.DELEGATES_TO_KEY
import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.DELEGATES_TO_STRATEGY_KEY
import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.DelegatesToInfo
/**
@@ -148,7 +150,9 @@ class GradleExtensionsContributor : GradleMethodContextContributor {
if (parent is GrMethodCallExpressionImpl && parent.argumentList.namedArguments.isNotEmpty()) {
addParameter("args", JAVA_UTIL_MAP, true)
}
addParameter("configuration", GROOVY_LANG_CLOSURE, true)
val closureParam = addAndGetParameter("configuration", GROOVY_LANG_CLOSURE, true)
closureParam.putUserData(DELEGATES_TO_KEY, gradleTask.typeFqn)
closureParam.putUserData(DELEGATES_TO_STRATEGY_KEY, Closure.OWNER_FIRST)
}
if (!processor.execute(methodBuilder, state)) return false
break
@@ -45,8 +45,8 @@ class GradleMiscContributor : GradleMethodContextContributor {
val useJUnitClosure = groovyClosure().inMethod(psiMethod(GRADLE_API_TASKS_TESTING_TEST, "useJUnit"))
val testLoggingClosure = groovyClosure().inMethod(psiMethod(GRADLE_API_TASKS_TESTING_TEST, "testLogging"))
val downloadClosure = groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "download"))
val domainCollectionWithTypeClosure = groovyClosure().inMethod(psiMethod("org.gradle.api.DomainObjectCollection", "withType"))
val domainCollectionWithTypeClosure = groovyClosure().inMethod(psiMethod(GRADLE_API_DOMAIN_OBJECT_COLLECTION, "withType"))
// val publicationsClosure = groovyClosure().inMethod(psiMethod("org.gradle.api.publish.PublishingExtension", "publications"))
val downloadSpecFqn = "de.undercouch.gradle.tasks.download.DownloadSpec"
val pluginDependenciesSpecFqn = "org.gradle.plugin.use.PluginDependenciesSpec"
}
@@ -61,6 +61,9 @@ class GradleMiscContributor : GradleMethodContextContributor {
if (downloadClosure.accepts(closure)) {
return DelegatesToInfo(TypesUtil.createType(downloadSpecFqn, closure), Closure.DELEGATE_FIRST)
}
// if (publicationsClosure.accepts(closure)) {
// return DelegatesToInfo(TypesUtil.createType("org.gradle.api.publish.PublicationContainer", closure), Closure.DELEGATE_FIRST)
// }
if (domainCollectionWithTypeClosure.accepts(closure)) {
val parent = closure.parent
@@ -126,14 +126,15 @@ class GradleNonCodeMembersContributor : NonCodeMembersContributor() {
if (GradleResolverUtil.canBeMethodOf("set" + propCandidate.capitalize(), psiClass)) return
val closure = PsiTreeUtil.getParentOfType(place, GrClosableBlock::class.java)
if (closure != null) {
val info = getDelegatesToInfo(closure)
if (info != null) {
val fqNameToDelegate = TypesUtil.getQualifiedName(info.typeToDelegate) ?: return
val classToDelegate = psiManager.findClassWithCache(fqNameToDelegate, place.resolveScope) ?: return
if (GradleResolverUtil.canBeMethodOf(propCandidate, classToDelegate)) return
if (GradleResolverUtil.canBeMethodOf("get" + propCandidate.capitalize(), classToDelegate)) return
if (GradleResolverUtil.canBeMethodOf("set" + propCandidate.capitalize(), classToDelegate)) return
val typeToDelegate = closure?.let { getDelegatesToInfo(it)?.typeToDelegate }
if (typeToDelegate != null) {
val fqNameToDelegate = TypesUtil.getQualifiedName(typeToDelegate) ?: return
val classToDelegate = psiManager.findClassWithCache(fqNameToDelegate, place.resolveScope) ?: return
if (classToDelegate !== aClass) {
val parent = place.parent
if (parent is GrMethodCall) {
if (canBeMethodOf(propCandidate, parent, typeToDelegate)) return
}
}
}
@@ -158,10 +159,10 @@ class GradleNonCodeMembersContributor : NonCodeMembersContributor() {
val wrappedBase = GrLightMethodBuilder(place.manager, "configure").apply {
returnType = domainObjectType
containingClass = aClass
addParameter("configureClosure", GROOVY_LANG_CLOSURE, true).apply {
putUserData(DELEGATES_TO_KEY, domainObjectFqn)
putUserData(DELEGATES_TO_STRATEGY_KEY, Closure.DELEGATE_FIRST)
}
val closureParam = addAndGetParameter("configuration", GROOVY_LANG_CLOSURE, true)
closureParam.putUserData(DELEGATES_TO_KEY, domainObjectFqn)
closureParam.putUserData(DELEGATES_TO_STRATEGY_KEY, Closure.OWNER_FIRST)
val method = aClass.findMethodsByName("create", true).firstOrNull { it.parameterList.parametersCount == argsCount }
if (method != null) navigationElement = method
}
@@ -18,11 +18,23 @@ package org.jetbrains.plugins.gradle.service.resolve
import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.plugins.gradle.util.GradleConstants.EXTENSION
import org.jetbrains.plugins.groovy.codeInspection.assignment.GrMethodCallInfo
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock
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.GroovyPsiManager
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass
import org.jetbrains.plugins.groovy.lang.psi.patterns.GroovyPatterns
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil
import org.jetbrains.plugins.groovy.lang.resolve.NON_CODE
import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil.processAllDeclarations
import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.getDelegatesToInfo
import org.jetbrains.plugins.groovy.lang.resolve.processors.AccessorResolverProcessor
import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint
import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor
import java.util.*
/**
@@ -44,6 +56,23 @@ fun processDeclarations(aClass: PsiClass,
aClass.processDeclarations(processor, state, null, place)
}
else {
val propCandidate = place.references.singleOrNull()?.canonicalText
if (propCandidate != null) {
val closure = PsiTreeUtil.getParentOfType(place, GrClosableBlock::class.java)
val typeToDelegate = closure?.let { getDelegatesToInfo(it)?.typeToDelegate }
if (typeToDelegate != null) {
val fqNameToDelegate = TypesUtil.getQualifiedName(typeToDelegate) ?: return true
val classToDelegate = GroovyPsiManager.getInstance(place.project).findClassWithCache(fqNameToDelegate,
place.resolveScope) ?: return true
if (classToDelegate !== aClass) {
val parent = place.parent
if (parent is GrMethodCall) {
if (canBeMethodOf(propCandidate, parent, typeToDelegate)) return true
}
}
}
}
val lValue: Boolean = place is GrReferenceExpression && PsiUtil.isLValue(place);
if (!lValue) {
val isSetterCandidate = name.startsWith("set")
@@ -57,7 +86,7 @@ fun processDeclarations(aClass: PsiClass,
if (!processor.execute(method, state)) return false
}
for (method in aClass.findMethodsByName("set" + propertyName.capitalize(), true)) {
if(PsiType.VOID != method.returnType) continue
if (PsiType.VOID != method.returnType) continue
if (processedSignatures.contains(method.getSignature(PsiSubstitutor.EMPTY).parameterTypes.map({ it.canonicalText }))) continue
processedSignatures.add(method.getSignature(PsiSubstitutor.EMPTY).parameterTypes.map({ it.canonicalText }))
place.putUserData(RESOLVED_CODE, true)
@@ -88,4 +117,41 @@ fun processDeclarations(aClass: PsiClass,
return true
}
fun psiMethodInClass(containingClass: String) = GroovyPatterns.psiMethod().definedInClass(containingClass)
fun canBeMethodOf(methodName: String,
place: GrMethodCall,
type: PsiType): Boolean {
val methodCallInfo = GrMethodCallInfo(place)
val invoked = methodCallInfo.invokedExpression ?: return false
val argumentTypes = methodCallInfo.argumentTypes
val thisType = TypesUtil.boxPrimitiveType(type, place.manager, place.resolveScope)
val processor = MethodResolverProcessor(methodName, invoked, false, thisType, argumentTypes, PsiType.EMPTY_ARRAY, false)
val state = ResolveState.initial().let {
it.put(ClassHint.RESOLVE_CONTEXT, invoked)
it.put(NON_CODE, false)
}
processAllDeclarations(thisType, processor, state, invoked)
val hasApplicableMethods = processor.hasApplicableCandidates()
if (hasApplicableMethods) {
return true
}
//search for getters
for (getterName in GroovyPropertyUtils.suggestGettersName(methodName)) {
val getterResolver = AccessorResolverProcessor(getterName, methodName, invoked, true, thisType, PsiType.EMPTY_ARRAY)
processAllDeclarations(thisType, getterResolver, state, invoked)
if (getterResolver.hasApplicableCandidates()) {
return true
}
}
//search for setters
for (setterName in GroovyPropertyUtils.suggestSettersName(methodName)) {
val getterResolver = AccessorResolverProcessor(setterName, methodName, invoked, false, thisType, PsiType.EMPTY_ARRAY)
processAllDeclarations(thisType, getterResolver, state, invoked)
if (getterResolver.hasApplicableCandidates()) {
return true
}
}
return false
}