gradle / kotlin-dsl-samples

Samples builds using the Gradle Kotlin DSL
https://gradle.org/kotlin/
Other
3.71k stars 434 forks source link

Implicit SAM to receiver conversion override the use of variables defined in an enclosing scope #1257

Closed Fiouz closed 5 years ago

Fiouz commented 6 years ago

Expected Behavior

$ gradlew -b build.gradle.kts build
Picked up JAVA_TOOL_OPTIONS: "-XX:-UsePerfData" "-Duser.language=en" "-Duser.country=US" "-Duser.timezone=UTC" "-Dfile.encoding=UTF-8"

> Task :display1
Description for 'task ':display1'': common description

> Task :display2
Description for 'task ':display2'': common description

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

Current Behavior

$ gradlew -b build.gradle.kts build
Picked up JAVA_TOOL_OPTIONS: "-XX:-UsePerfData" "-Duser.language=en" "-Duser.country=US" "-Duser.timezone=UTC" "-Dfile.encoding=UTF-8"

> Task :display1
Description for 'task ':display1'': null

> Task :display2
Description for 'task ':display2'': null

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

Context

// build.gradle.kts (Kotlin syntax)
apply {
    plugin("base")
}
val description = "common description"
val task1 = tasks.register("display1") {
    this.description = description

    doLast("display description") {
        logger.lifecycle("Description for '{}': {}", this, this.description)
    }
}
val task2 = tasks.register("display2") {
    this.description = description

    doLast("display description") {
        logger.lifecycle("Description for '{}': {}", this, this.description)
    }
}
tasks.named(LifecycleBasePlugin.BUILD_TASK_NAME).configure {
    dependsOn(task1, task2)
}

Shadowing enclosing variables is a problem when Gradle (or any plugin) starts adding supposedly non API-breaking changes (minor version update) such as adding fields to model that are subject to implicit receiver conversion: suddenly the build script would stop working as expected if it happens to have defined a local variable with the same name.

Steps to Reproduce (for bugs)

Your Environment

Task :buildEnvironment


Root project

classpath No dependencies

A web-based, searchable dependency report is available by adding the --scan option.

BUILD SUCCESSFUL in 1s 1 actionable task: 1 executed

$ gradlew --version Picked up JAVA_TOOL_OPTIONS: "-XX:-UsePerfData" "-Duser.language=en" "-Duser.country=US" "-Duser.timezone=UTC" "-Dfile.encoding=UTF-8"


Gradle 4.10.2

Build time: 2018-09-19 18:10:15 UTC Revision: b4d8d5d170bb4ba516e88d7fe5647e2323d791dd

Kotlin DSL: 1.0-rc-6 Kotlin: 1.2.61 Groovy: 2.4.15 Ant: Apache Ant(TM) version 1.9.11 compiled on March 23 2018 JVM: 1.8.0_192 (Oracle Corporation 25.192-b12) OS: Windows 10 10.0 amd64


It should be noted that the Groovy implementation does not have this issue (closure delegate does not shadow variables defined in an enclosing scope):
```groovy
// build.gradle (Groovy syntax)
apply {
    plugin("base")
}
def description = "common description"
def task1 = tasks.register("display1") {
    delegate.description = description

    doLast("display description") {
        logger.lifecycle("Description for '{}': {}", delegate, delegate.description)
    }
}
def task2 = tasks.register("display2") {
    delegate.description = description

    doLast("display description") {
        logger.lifecycle("Description for '{}': {}", delegate, delegate.description)
    }
}
tasks.named(LifecycleBasePlugin.BUILD_TASK_NAME).configure {
    dependsOn(task1, task2)
}
$ gradlew -b build.gradle build
Picked up JAVA_TOOL_OPTIONS: "-XX:-UsePerfData" "-Duser.language=en" "-Duser.country=US" "-Duser.timezone=UTC" "-Dfile.encoding=UTF-8"

> Task :display1
Description for 'task ':display1'': common description

> Task :display2
Description for 'task ':display2'': common description

BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

Unless there is some language semantic/Gradle workaround that should be used here, the core of the problem may be in Kotlin but the heavy use of implicit receiver for org.gradle.api.Action from this project make it a bigger issue for Gradle users where it does not seem we can disable those implicit SAM to receiver conversions.

Note the example here is trivial but the issue can happen with any API manipulated within the build file.

JLLeitschuh commented 6 years ago

I'm going to guess this is not specific to the Gradle Kotlin DSL. I think this is more about how the Kotlin compiler handles scope.

eskatos commented 5 years ago

Thank you for the great report @Fiouz!

You got it right, this is expected Kotlin language behavior.

We understand might cause some confusion at first but the benefits of having the implicit receiver outweigh the initial cost.