From d191022332df6436f9fa77c7d7269088c1b8cfaf Mon Sep 17 00:00:00 2001 From: Nikita Biriukov Date: Sun, 31 Mar 2024 21:58:15 +0200 Subject: [PATCH] [gradle][groovy] IDEA-257715 make GradleDelegatesToProvider responsible for delegate resolving in Project's Closures. Previously, GradleProjectContributor was responsible for delegate resolving in closures of several Project methods. It was a hardcoded logic: if Closure is a parameter of method foo() from class Project, then let delegate be CLASS_NAME. The new logic implemented in GradleDelegatesToProvider is more flexible. It allows determining a delegate if a called method with Closure has overloaded method with Action. It covers almost all the cases supported by GradleProjectContributor, and gets triggered before it. So all covered cases were removed from there, except the delegate resolving for Project#configure(...) method. Project#configure(...) has a version with Action parameter, but it's not possible to surely resolve T if the version of this method with Closure was called. This commit also has tests covering considered cases for Project. GitOrigin-RevId: e1088ad60a61868856aca4dc0b8e943bc7e04e5f --- .../resolve/GradleDelegatesToProvider.kt | 4 +- .../resolve/GradleProjectContributor.kt | 23 +------ .../java/testSources/dsl/GradleProjectTest.kt | 67 ++++++++++++++++++- 3 files changed, 71 insertions(+), 23 deletions(-) diff --git a/plugins/gradle/java/src/service/resolve/GradleDelegatesToProvider.kt b/plugins/gradle/java/src/service/resolve/GradleDelegatesToProvider.kt index b01396821698..7266df544b30 100644 --- a/plugins/gradle/java/src/service/resolve/GradleDelegatesToProvider.kt +++ b/plugins/gradle/java/src/service/resolve/GradleDelegatesToProvider.kt @@ -70,7 +70,9 @@ class GradleDelegatesToProvider : GrDelegatesToProvider { return delegate } val fqClassName = delegate.canonicalText - if (fqClassName != GRADLE_API_ARTIFACT_HANDLER) { + if (fqClassName != GRADLE_API_ARTIFACT_HANDLER + && fqClassName != GRADLE_API_PROJECT + ) { return delegate } val type = createType(fqClassName, expression) diff --git a/plugins/gradle/java/src/service/resolve/GradleProjectContributor.kt b/plugins/gradle/java/src/service/resolve/GradleProjectContributor.kt index 4128b04b5db1..06e3ad4a335a 100644 --- a/plugins/gradle/java/src/service/resolve/GradleProjectContributor.kt +++ b/plugins/gradle/java/src/service/resolve/GradleProjectContributor.kt @@ -1,13 +1,10 @@ // Copyright 2000-2019 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.gradle.service.resolve -import com.intellij.psi.PsiClassType -import com.intellij.psi.PsiType import com.intellij.util.ProcessingContext import groovy.lang.Closure -import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.* +import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.GRADLE_API_PROJECT import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil.createType import org.jetbrains.plugins.groovy.lang.psi.patterns.GroovyClosurePattern import org.jetbrains.plugins.groovy.lang.psi.patterns.groovyClosure @@ -20,12 +17,8 @@ import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.DelegatesToInfo class GradleProjectContributor : GradleMethodContextContributor { companion object { val projectClosure: GroovyClosurePattern = groovyClosure().inMethod( - psiMethod(GRADLE_API_PROJECT, "project", "configure", "subprojects", "allprojects") + psiMethod(GRADLE_API_PROJECT, "configure") ).inMethodResult(saveProjectType) - val copySpecClosure: GroovyClosurePattern = groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "copy", "copySpec")) - val fileTreeClosure: GroovyClosurePattern = groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "fileTree")) - val filesClosure: GroovyClosurePattern = groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "files")) - val execClosure: GroovyClosurePattern = groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "exec")) } override fun getDelegatesToInfo(closure: GrClosableBlock): DelegatesToInfo? { @@ -34,18 +27,6 @@ class GradleProjectContributor : GradleMethodContextContributor { val projectType = createType(GRADLE_API_PROJECT, closure) return DelegatesToInfo(context[projectTypeKey]?.setType(projectType) ?: projectType, Closure.DELEGATE_FIRST) } - if (copySpecClosure.accepts(closure)) { - return DelegatesToInfo(createType(GRADLE_API_FILE_COPY_SPEC, closure), Closure.DELEGATE_FIRST) - } - if (fileTreeClosure.accepts(closure)) { - return DelegatesToInfo(createType(GRADLE_API_FILE_CONFIGURABLE_FILE_TREE, closure), Closure.DELEGATE_FIRST) - } - if (filesClosure.accepts(closure)) { - return DelegatesToInfo(createType(GRADLE_API_FILE_CONFIGURABLE_FILE_COLLECTION, closure), Closure.DELEGATE_FIRST) - } - if (execClosure.accepts(closure)) { - return DelegatesToInfo(createType(GRADLE_PROCESS_EXEC_SPEC, closure), Closure.DELEGATE_FIRST) - } return null } } diff --git a/plugins/gradle/java/testSources/dsl/GradleProjectTest.kt b/plugins/gradle/java/testSources/dsl/GradleProjectTest.kt index 31f3b1fb4963..f1c48d6c69b2 100644 --- a/plugins/gradle/java/testSources/dsl/GradleProjectTest.kt +++ b/plugins/gradle/java/testSources/dsl/GradleProjectTest.kt @@ -3,8 +3,9 @@ package org.jetbrains.plugins.gradle.dsl import com.intellij.psi.PsiMethod import com.intellij.testFramework.assertInstanceOf +import groovy.lang.Closure.DELEGATE_FIRST import org.gradle.util.GradleVersion -import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.GRADLE_API_PROJECT +import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.* import org.jetbrains.plugins.gradle.testFramework.GradleCodeInsightTestCase import org.jetbrains.plugins.gradle.testFramework.annotations.AllGradleVersionsSource import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall @@ -113,4 +114,68 @@ class GradleProjectTest : GradleCodeInsightTestCase() { } } } + + @ParameterizedTest + @AllGradleVersionsSource(""" + "project(':') {}", + "allprojects {}", + "subprojects {}", + "configure(project(':')) {}", + "configure([project(':')]) {}", + "beforeEvaluate {}", + "afterEvaluate {}" + """) + fun `resolve a delegate in Closures of methods providing Project's context`( + gradleVersion: GradleVersion, + expression: String + ) { + testEmptyProject(gradleVersion) { + testBuildscript(expression) { + closureDelegateTest(GRADLE_API_PROJECT, DELEGATE_FIRST) + } + } + } + + @ParameterizedTest + @AllGradleVersionsSource(PROJECT_CONTEXTS, """ + "copy{}", + "copySpec{}" + """) + fun `resolve a delegate in copy and copySpec Closures`(gradleVersion: GradleVersion, decorator: String, expression: String) { + testEmptyProject(gradleVersion) { + testBuildscript(decorator, expression) { + closureDelegateTest(GRADLE_API_FILE_COPY_SPEC, DELEGATE_FIRST) + } + } + } + + @ParameterizedTest + @AllGradleVersionsSource(PROJECT_CONTEXTS) + fun `resolve a delegate in fileTree Closure`(gradleVersion: GradleVersion, decorator: String) { + testEmptyProject(gradleVersion) { + testBuildscript(decorator, "fileTree('baseDir'){}") { + closureDelegateTest(GRADLE_API_FILE_CONFIGURABLE_FILE_TREE, DELEGATE_FIRST) + } + } + } + + @ParameterizedTest + @AllGradleVersionsSource(PROJECT_CONTEXTS) + fun `resolve a delegate in files Closure`(gradleVersion: GradleVersion, decorator: String) { + testEmptyProject(gradleVersion) { + testBuildscript(decorator, "files('paths'){}") { + closureDelegateTest(GRADLE_API_FILE_CONFIGURABLE_FILE_COLLECTION, DELEGATE_FIRST) + } + } + } + + @ParameterizedTest + @AllGradleVersionsSource(PROJECT_CONTEXTS) + fun `resolve a delegate in exec Closure`(gradleVersion: GradleVersion, decorator: String) { + testEmptyProject(gradleVersion) { + testBuildscript(decorator, "exec{}") { + closureDelegateTest(GRADLE_PROCESS_EXEC_SPEC, DELEGATE_FIRST) + } + } + } } \ No newline at end of file