From 305fba4b6543f4ad8b04ed69739d770fb2eeb283 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Tue, 23 Apr 2019 21:24:54 +0300 Subject: [PATCH] [gradle] dsl: support binary operator task declaration GitOrigin-RevId: b7a22b66134927e1c7afc47da687dd8aa3d59315 --- .../META-INF/gradle-groovy-integration.xml | 4 +- .../src/service/resolve/taskDeclaration.kt | 87 +++++++++++++++++++ .../src/service/resolve/taskIdReference.kt | 37 -------- .../testSources/dsl/GradleTasksTest.groovy | 6 ++ 4 files changed, 95 insertions(+), 39 deletions(-) create mode 100644 plugins/gradle/java/src/service/resolve/taskDeclaration.kt delete mode 100644 plugins/gradle/java/src/service/resolve/taskIdReference.kt diff --git a/plugins/gradle/java/resources/META-INF/gradle-groovy-integration.xml b/plugins/gradle/java/resources/META-INF/gradle-groovy-integration.xml index f69c493db1e6..b013423ef4b6 100644 --- a/plugins/gradle/java/resources/META-INF/gradle-groovy-integration.xml +++ b/plugins/gradle/java/resources/META-INF/gradle-groovy-integration.xml @@ -70,9 +70,9 @@ - + + implementationClass="org.jetbrains.plugins.gradle.service.resolve.GradleTaskDeclarationTypeCalculator"/> diff --git a/plugins/gradle/java/src/service/resolve/taskDeclaration.kt b/plugins/gradle/java/src/service/resolve/taskDeclaration.kt new file mode 100644 index 000000000000..4e5b79590af3 --- /dev/null +++ b/plugins/gradle/java/src/service/resolve/taskDeclaration.kt @@ -0,0 +1,87 @@ +// 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.CommonClassNames.JAVA_LANG_STRING +import com.intellij.psi.PsiType +import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.GRADLE_API_TASK +import org.jetbrains.plugins.groovy.lang.GroovyExpressionFilter +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression +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.literals.GrLiteral +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil.createType +import org.jetbrains.plugins.groovy.lang.typing.GrTypeCalculator + +class GradleTaskDeclarationExpressionFilter : GroovyExpressionFilter { + + override fun isFake(expression: GrExpression): Boolean = when (expression) { + is GrReferenceExpression -> isTaskIdExpression(expression) || isTaskIdExpressionInBinaryOp(expression) + is GrMethodCall -> isFakeTaskCall(expression) + else -> false + } +} + +class GradleTaskDeclarationTypeCalculator : GrTypeCalculator { + + override fun getType(expression: GrReferenceExpression): PsiType? = when { + isTaskIdExpression(expression) -> createType(JAVA_LANG_STRING, expression.containingFile) + isTaskIdExpressionInBinaryOp(expression) -> createType(GRADLE_API_TASK, expression.containingFile) + else -> null + } +} + +/** + * Matches `id` in: `task id` + * which gets transformed to: `task("id")` + */ +private fun isTaskIdExpression(expression: GrReferenceExpression): Boolean { + if (expression.isQualified) return false + if (!isFirstArgumentOfTaskMethod(expression)) return false + if (!expression.containingFile.isGradleScript()) return false + if (expression.staticReference.resolve() != null) return false + return true +} + +/** + * Matches `id` in: `task id right` + * which gets transformed to: `task("id") right` + */ +private fun isTaskIdExpressionInBinaryOp(expression: GrReferenceExpression): Boolean { + if (expression.isQualified) return false + val binary = expression.parent as? GrBinaryExpression ?: return false + if (expression != binary.leftOperand) return false + if (!isFirstArgumentOfTaskMethod(binary)) return false + if (!expression.containingFile.isGradleScript()) return false + return true +} + +private fun isFirstArgumentOfTaskMethod(expression: GrExpression): Boolean { + val argumentList = expression.parent as? GrArgumentList ?: return false + if (expression != argumentList.expressionArguments[0]) return false + val methodCall = argumentList.parent as? GrMethodCall ?: return false + return isTaskCall(methodCall) +} + +/** + * Matches `task` in: `task id right` + * which gets transformed to: `task("id") right` + */ +private fun isFakeTaskCall(expression: GrMethodCall): Boolean { + return isTaskCall(expression) && isCallWithSingleBinaryExpressionArgument(expression) +} + +private fun isTaskCall(methodCall: GrMethodCall): Boolean { + val invokedExpression = methodCall.invokedExpression as? GrReferenceExpression ?: return false + return invokedExpression.referenceName == "task" && !invokedExpression.isQualified +} + +private fun isCallWithSingleBinaryExpressionArgument(methodCall: GrMethodCall): Boolean = + methodCall.namedArguments.isEmpty() && + methodCall.closureArguments.isEmpty() && + methodCall.expressionArguments.singleOrNull().let { argument -> + argument is GrBinaryExpression && argument.leftOperand.let { left -> + left is GrLiteral || left is GrReferenceExpression && !left.isQualified + } + } diff --git a/plugins/gradle/java/src/service/resolve/taskIdReference.kt b/plugins/gradle/java/src/service/resolve/taskIdReference.kt deleted file mode 100644 index 9e7ed79d0a0b..000000000000 --- a/plugins/gradle/java/src/service/resolve/taskIdReference.kt +++ /dev/null @@ -1,37 +0,0 @@ -// 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.CommonClassNames.JAVA_LANG_STRING -import com.intellij.psi.PsiType -import org.jetbrains.plugins.groovy.lang.GroovyExpressionFilter -import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList -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.impl.statements.expressions.TypesUtil.createType -import org.jetbrains.plugins.groovy.lang.typing.GrTypeCalculator - -class GradleTaskIdExpressionFilter : GroovyExpressionFilter { - - override fun isFake(expression: GrExpression): Boolean { - return expression is GrReferenceExpression && isTaskIdExpression(expression) - } -} - -class GradleTaskIdTypeCalculator : GrTypeCalculator { - - override fun getType(expression: GrReferenceExpression): PsiType? { - return if (isTaskIdExpression(expression)) createType(JAVA_LANG_STRING, expression.containingFile) else null - } -} - -private fun isTaskIdExpression(expression: GrReferenceExpression): Boolean { - if (expression.isQualified) return false - val argumentList = expression.parent as? GrArgumentList ?: return false - if (expression != argumentList.expressionArguments[0]) return false - val methodCall = argumentList.parent as? GrMethodCall ?: return false - val invokedExpression = methodCall.invokedExpression as? GrReferenceExpression ?: return false - if (invokedExpression.referenceName != "task") return false - if (invokedExpression.isQualified) return false - return expression.containingFile.isGradleScript() && expression.staticReference.resolve() == null -} diff --git a/plugins/gradle/java/testSources/dsl/GradleTasksTest.groovy b/plugins/gradle/java/testSources/dsl/GradleTasksTest.groovy index 49743eef530b..c350b725b821 100644 --- a/plugins/gradle/java/testSources/dsl/GradleTasksTest.groovy +++ b/plugins/gradle/java/testSources/dsl/GradleTasksTest.groovy @@ -115,6 +115,8 @@ def t2_ = task, ?)'">(description: 'mark', {}, idt4_) +def t5 = task idt5 << {} +def t6 = task idt6 + {} def insideClosure = { def ct1 = task(cidt1) @@ -123,6 +125,8 @@ def insideClosure = { def ct3 = task(description: 'oh', cidt3) def ct4 = task(description: 'hi', cidt4, {}) def ct4_ = task(description: 'mark', {}, cidt4_) + def ct5 = task cidt5 << {} + def ct6 = task cidt6 + {} } insideClosure() @@ -133,6 +137,8 @@ def insideMethod() { def mt3 = task(description: 'oh', midt3) def mt4 = task(description: 'hi', midt4, {}) def mt4_ = task(description: 'mark', {}, midt4_) + def mt5 = task midt5 << {} + def mt6 = task midt6 + {} } insideMethod()