Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scala 2.13 Scalafix: ExplicitNonNullaryApply - Auto-application to () is deprecated #537

Conversation

rtyley
Copy link
Member

@rtyley rtyley commented Oct 22, 2024

Scala 2.13 deprecates (with PR scala/scala#8833) the old behaviour of Scala that zero-parameter methods could be called with either one or zero pairs of parenthesis - ie if you have a method def foo() you could call it as foo() or just foo. With Scala 3, you have to match the number of brackets used in the method declaration when you call it - so you'd have to use foo() or you'd get an error like this:

[error] ~/code/presence-indicator/app/actor/OpenSocketActor.scala:79:7: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method sender,
[error] or remove the empty argument list from its definition (Java-defined methods are exempt).
[error] In Scala 3, an unapplied method like this will be eta-expanded into a function. [quickfixable]
[error]       sender ! Map(serverId -> (LiveActors(connectionPing, subscription)))
[error]       ^

Java methods are exempt from this restriction - you can call either hashCode() (which, in Java, is how the method has to be defined, with empty brackets) or just hashCode (which is how that method would have been declared if it was declared in Scala, in Scala methods with no side-effects should be declared without brackets: https://docs.scala-lang.org/style/method-invocation.html#arity-0).

Automatically fixing this code issue

There are two possible ways of automating this code fix - in this small project, they both produce the same code changes:

Fixing if the project is already on Scala 2.13 - use -quickfix in scalac

You can use the -quickfix support added to Scala 2.13.12 with scala/scala#10482:

Add either of these to the scalacOptions in build.sbt:

  • "-quickfix:any" ...to apply all available quick fixes
  • "-quickfix:msg=Auto-application" ...to apply quick fixes where the message contains "Auto-application"

Then run compile on the sbt console - the first compile will still fail, but it will subtly change the error message to say [rewritten by -quickfix] - your files have been edited to receive the fix:

[error] /Users/Roberto_Tyley/code/presence-indicator/app/actor/OpenSocketActor.scala:79:7: [rewritten by -quickfix] Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method sender,
[error] or remove the empty argument list from its definition (Java-defined methods are exempt).
[error] In Scala 3, an unapplied method like this will be eta-expanded into a function.
[error]       sender ! Map(serverId -> (LiveActors(connectionPing, subscription)))
[error]       ^

...run compile a second time, and compiler will be much happier.

Examples of other PRs using -quickfix to fix this code issue:

Fixing while still on Scala 2.12 - use Scalafix

Fixing this everywhere in a project can be tedious, but thankfully there is a ExplicitNonNullaryApply Scalafix rule to fix this in the https://github.com/lightbend-labs/scala-rewrites project.

The Scalafix rule needs to be run while the project is still on Scala 2.12, not Scala 2.13 (otherwise sbt will say: "Error downloading ch.epfl.scala:sbt-scalafix;sbtVersion=1.0;scalaVersion=2.13:0.13.0").

Once the Scalafix plugin is made available to sbt (by adding addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.13.0") to either project/plugins.sbt or ~/.sbt/1.0/plugins.sbt), you can run these commands on the sbt prompt to automatically generate the changes in this PR:

scalafixEnable
scalafixAll dependency:[email protected]:scala-rewrites:0.1.5

Examples of other PRs using Scalafix to fix this code issue:

See also:

…`()` is deprecated

Scala 2.13 deprecates (with PR scala/scala#8833) the old behaviour of Scala that zero-parameter methods could be called with either one or zero pairs of parenthesis - ie if you have a method `def foo()` you could call it as `foo()` or just `foo`. With Scala 3, you have to match the number of brackets *used in the method declaration* when you call it - so you'd _have_ to use `foo()` or you'd get an error like this:

```
[error] ~/code/presence-indicator/app/actor/OpenSocketActor.scala:79:7: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method sender,
[error] or remove the empty argument list from its definition (Java-defined methods are exempt).
[error] In Scala 3, an unapplied method like this will be eta-expanded into a function. [quickfixable]
[error]       sender ! Map(serverId -> (LiveActors(connectionPing, subscription)))
[error]       ^
```

Java methods are exempt from this restriction - you can call either `hashCode()` (which, in Java, is how the method _has_ to be defined, with empty brackets) or just `hashCode` (which is how that method would have been declared if it was declared in Scala, in Scala methods with no side-effects should be declared without brackets: https://docs.scala-lang.org/style/method-invocation.html#arity-0).

## Automatically fixing this code issue

There are two possible ways of automating this code fix - in this small project, they both produce the same code changes:

### Fixing if the project is already on Scala 2.13 - use `-quickfix` in `scalac`

You can use the `-quickfix` support added to Scala 2.13.12 with scala/scala#10482:

Add either of these to the `scalacOptions` in `build.sbt`:

* `"-quickfix:any"`  ...to apply *all* available quick fixes
* `"-quickfix:msg=Auto-application"`  ...to apply quick fixes where the message contains "Auto-application"

Then run `compile` on the sbt console - the first compile will still fail, but it will subtly change the error message to say `[rewritten by -quickfix]` - your files have been edited to receive the fix:

```
[error] /Users/Roberto_Tyley/code/presence-indicator/app/actor/OpenSocketActor.scala:79:7: [rewritten by -quickfix] Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method sender,
[error] or remove the empty argument list from its definition (Java-defined methods are exempt).
[error] In Scala 3, an unapplied method like this will be eta-expanded into a function.
[error]       sender ! Map(serverId -> (LiveActors(connectionPing, subscription)))
[error]       ^
```

...run `compile` a second time, and compiler will be much happier.

Examples of other PRs using `-quickfix` to fix this code issue:

* guardian/ophan#5719

### Fixing while still on Scala 2.12 - use Scalafix

Fixing this everywhere in a project can be tedious, but thankfully there is a `ExplicitNonNullaryApply` Scalafix rule to fix this in the https://github.com/lightbend-labs/scala-rewrites project.

The Scalafix rule needs to be run while the project is still on Scala 2.12, not Scala 2.13 (otherwise sbt will say: "Error downloading ch.epfl.scala:sbt-scalafix;sbtVersion=1.0;scalaVersion=2.13:0.13.0").

Once the Scalafix plugin is made available to sbt (by adding `addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.13.0")`
to either `project/plugins.sbt` or `~/.sbt/1.0/plugins.sbt`), you can run these commands on the sbt prompt to automatically generate the changes in this PR:

```
scalafixEnable
scalafixAll dependency:[email protected]:scala-rewrites:0.1.5
```

Examples of other PRs using Scalafix to fix this code issue:

* guardian/mobile-apps-api#2728
* guardian/presence-indicator#196

See also:

* scalacenter/scalafix#204
* lightbend-labs/scala-rewrites#14
@rtyley rtyley marked this pull request as ready for review October 22, 2024 15:40
@rtyley rtyley merged commit b3dabeb into main Oct 22, 2024
3 checks passed
@rtyley rtyley deleted the scala-2.13-handle-deprecation-of-auto-application-for-zero-parameter-methods branch October 22, 2024 15:50
@prout-bot
Copy link

Seen on PROD (merged by @rtyley 6 minutes and 22 seconds ago) Please check your changes!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants