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

Any and Nothing fail with play-json in Scala 2.13 #454

Open
nehaev opened this issue Jul 30, 2019 · 6 comments
Open

Any and Nothing fail with play-json in Scala 2.13 #454

nehaev opened this issue Jul 30, 2019 · 6 comments

Comments

@nehaev
Copy link

nehaev commented Jul 30, 2019

Any and Nothing fail on Json.reads macro for case classes with more than one param. On Scala 2.12 same code compiles fine.

Build settings:

scalaVersion := "2.13.0"
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.7.4"
wartremoverErrors in (Compile, compile) ++= Warts.all

Example code:

import play.api.libs.json._

final case class Greeting(id: Long, greeting: String)

object Greeting {
  implicit val reads: Reads[Greeting] = Json.reads[Greeting]
}

Error:

[error] [wartremover:Any] Inferred type containing Any
[error]   implicit val reads: Reads[Greeting] = Json.reads[Greeting]
[error]                                                   ^

Output of -Xprint:typer for the part which is presumably responsible for this:

scala 2.13: val underlying: play.api.libs.json.Reads[example.Greeting] = play.api.libs.functional.syntax.`package`.toFunctionalBuilderOps[play.api.libs.json.Reads, Long](play.api.libs.json.JsPath.\(json.this.JsonConfiguration.default[play.api.libs.json.Json.MacroOptions](MacroOptions.this.Default.macroOptionsDefault).naming.apply("id")).read[Long](json.this.Reads.LongReads))(functional.this.FunctionalCanBuild.functionalCanBuildApplicative[[A]play.api.libs.json.Reads[A]](json.this.Reads.applicative(json.this.JsResult.applicativeJsResult))).and[String](play.api.libs.json.JsPath.\(json.this.JsonConfiguration.default[play.api.libs.json.Json.MacroOptions](MacroOptions.this.Default.macroOptionsDefault).naming.apply("greeting")).read[String](json.this.Reads.StringReads)).apply[example.Greeting](((id: Long, greeting: String) => Greeting.apply(id, greeting)))(json.this.Reads.functorReads(json.this.Reads.applicative(json.this.JsResult.applicativeJsResult)));
scala 2.12: val underlying: play.api.libs.json.Reads[example.Greeting] = play.api.libs.functional.syntax.`package`.toFunctionalBuilderOps[play.api.libs.json.Reads, Long](play.api.libs.json.JsPath.\(json.this.JsonConfiguration.default[play.api.libs.json.Json.MacroOptions](MacroOptions.this.Default.macroOptionsDefault).naming.apply("id")).read[Long](json.this.Reads.LongReads))(functional.this.FunctionalCanBuild.functionalCanBuildApplicative[play.api.libs.json.Reads](json.this.Reads.applicative(json.this.JsResult.applicativeJsResult))).and[String](play.api.libs.json.JsPath.\(json.this.JsonConfiguration.default[play.api.libs.json.Json.MacroOptions](MacroOptions.this.Default.macroOptionsDefault).naming.apply("greeting")).read[String](json.this.Reads.StringReads)).apply[example.Greeting]({((id: Long, greeting: String) => Greeting.apply(id, greeting))})(json.this.Reads.functorReads(json.this.Reads.applicative(json.this.JsResult.applicativeJsResult)));
@nehaev
Copy link
Author

nehaev commented Aug 1, 2019

Minimized example.

The code below compiles fine even on Scala 2.13:

  import play.api.libs.json.{ JsPath, Reads }
  import play.api.libs.functional.FunctionalCanBuild
  import play.api.libs.functional.syntax
  val x = syntax.toFunctionalBuilderOps((JsPath \ "id").read[Long])(FunctionalCanBuild.functionalCanBuildApplicative[Reads])

From WartTraverser perspective it looks like:

...
class scala.reflect.internal.Trees$Select >>> play.api.libs.functional.FunctionalCanBuild.functionalCanBuildApplicative
class scala.reflect.internal.Trees$Select >>> play.api.libs.functional.FunctionalCanBuild
class scala.reflect.internal.Trees$Select >>> play.api.libs.functional
class scala.reflect.internal.Trees$Select >>> play.api.libs
class scala.reflect.internal.Trees$Select >>> play.api
class scala.reflect.internal.Trees$Ident >>> play
class scala.reflect.internal.Trees$TypeTree >>> play.api.libs.json.Reads
class scala.reflect.internal.Trees$ApplyToImplicitArgs >>> json.this.Reads.applicative(json.this.JsResult.applicativeJsResult)
...

The following code is generated by macros and causes Inferred type containing Any on Scala 2.13:

  import play.api.libs.json.{ JsPath, Reads }
  import play.api.libs.functional.FunctionalCanBuild
  import play.api.libs.functional.syntax
  val x = syntax.toFunctionalBuilderOps((JsPath \ "id").read[Long])

From WartTraverser perspective it looks like:

...
class scala.reflect.internal.Trees$Select >>> functional.this.FunctionalCanBuild.functionalCanBuildApplicative
class scala.reflect.internal.Trees$Select >>> functional.this.FunctionalCanBuild
class scala.reflect.internal.Trees$This >>> functional.this
class scala.reflect.internal.Trees$TypeTree >>> [A]play.api.libs.json.Reads[A]
class scala.reflect.internal.Types$PolyType xxx [A]play.api.libs.json.Reads[A]
class scala.reflect.internal.Trees$ApplyToImplicitArgs >>> json.this.Reads.applicative(json.this.JsResult.applicativeJsResult)
...

So this PolyType(typeParams = List(type A), resultType = play.api.libs.json.Reads[A]) is what causes the problem, because the base class for A is Any.

@nehaev
Copy link
Author

nehaev commented Aug 22, 2019

Does anyone have an idea how to fix this?

What comes to my mind is to ignore PolyType in https://github.com/wartremover/wartremover/blob/master/core/src/main/scala/wartremover/warts/ForbidInference.scala#L36 just like it's done for ExistentialType. But given that not only Any and Nothing use ForbidInference, I'm not sure that this change won't break anything else.

@mikail-khan
Copy link

I have the same issue. Bumped a project to Scala 2.13.2 and using play-json-derived-codecs (v 7.0.0) and wartremover complains:

Given this model:

sealed trait A extends Product with Serializable

object A{
  case object B extends A
  case object C extends A

  implicit val format: OFormat[A] = derived.oformat()
}

Wartremover complains :

 [wartremover: Any] Inferred type containing Any: julienrf.json.derived.DerivedReads[A]

Even trying:

implicit val format: OFormat[A] = derived.oformat[A]()

doesn't work

@mikail-khan
Copy link

mikail-khan commented Jun 8, 2020

Same occurs even for this:

final case class AuthenticatedRequest[A](request: MessagesRequest[A]) extends WrappedRequest[A](request)
[wartremover:Any] Inferred type containing Any: [A] AuthenticatedRequest[A]

@solarmosaic-kflorence
Copy link

solarmosaic-kflorence commented Jun 11, 2021

Same problem for a pretty simple play-json use case:

case class Response(name: String, data: Map[String, String])
object Response {
  implicit val format: Format[Response] = Json.format
}

Results in:

[wartremover:Any] Inferred type containing Any: [A]play.api.libs.json.Reads[A]

Strangely, it does not complain about:

case class Request(data: Map[String, String])
object Request {
  implicit val format: Format[Request] = Json.format
}

Time to slap @SuppressWarnings(Array("org.wartremover.warts.Any")) all over the place :(

@solarmosaic-kflorence
Copy link

solarmosaic-kflorence commented Jul 1, 2021

I have also seen:

[wartremover:Product] Inferred type containing Product: ProducerMessage with Product with Serializable
[error]   implicit val format: Format[ProducerMessage] = Json.format

Which has to be suppressed with:

@SuppressWarnings(Array("org.wartremover.warts.Product", "org.wartremover.warts.Serializable"))

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

No branches or pull requests

3 participants