diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleExtensionsContributor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleExtensionsContributor.kt index b8f54b3f44fe..da53c4c0faa8 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleExtensionsContributor.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleExtensionsContributor.kt @@ -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 diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleMiscContributor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleMiscContributor.kt index 35d8b7a021cb..27e991d14a20 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleMiscContributor.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleMiscContributor.kt @@ -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 diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleNonCodeMembersContributor.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleNonCodeMembersContributor.kt index 8b1350f38c19..4e2e370b6440 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleNonCodeMembersContributor.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleNonCodeMembersContributor.kt @@ -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 } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/util.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/util.kt index e94c12b666d5..13cef81c0ea4 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/util.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/util.kt @@ -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) \ No newline at end of file +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 +}