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()