scalameta / mdoc

Typechecked markdown documentation for Scala
https://scalameta.org/mdoc/
Apache License 2.0
394 stars 81 forks source link

any2stringadd deprecation warnings #210

Open vlovgr opened 4 years ago

vlovgr commented 4 years ago

With mdoc 2.0.2 and Scala 2.13, I'm sometimes seeing deprecation warnings for any2stringadd.

The following example is from modules.md in fs2-kafka.

warning: modules.md:138 (mdoc generated code) method any2stringadd in object Predef is deprecated (since 2.13.0): Implicit injection of + is deprecated. Convert to String to call + }; $doc.binder(res3, 0, 0, 22, 1)

olafurpg commented 4 years ago

Thank you for reporting! Can you maybe include the output from --verbose? This should print the fully instrumented code

vlovgr commented 4 years ago

@olafurpg I didn't know about --verbose, nice! Here's the instrumented code for the same file.

package repl
object Session extends _root_.mdoc.internal.document.DocumentBuilder {
  def app(): _root_.scala.Unit = {val _ = new App()}
  class App {
App0
}
object App0 {

$doc.startSection();
$doc.startStatement(0, 0, 0, 23);
import cats.implicits._
$doc.endStatement();
$doc.startStatement(1, 0, 1, 19);
import vulcan.Codec
$doc.endStatement();
$doc.startStatement(3, 0, 3, 55);
final case class Person(name: String, age: Option[Int])
$doc.endStatement();
$doc.startStatement(5, 0, 14, 3);
implicit val personCodec: Codec[Person] =
  Codec.record(
    name = "Person",
    namespace = Some("com.example")
  ) { field =>
    (
      field("name", _.name),
      field("age", _.age)
    ).mapN(Person(_, _))
  }; $doc.binder(personCodec, 5, 13, 5, 24)
$doc.endStatement();
$doc.endSection();

$doc.startSection();
$doc.startStatement(0, 0, 0, 21);
import cats.effect.IO
$doc.endStatement();
$doc.startStatement(1, 0, 1, 74);
import fs2.kafka.vulcan.{Auth, AvroSettings, SchemaRegistryClientSettings}
$doc.endStatement();
$doc.startStatement(3, 0, 7, 3);
val avroSettings =
  AvroSettings {
    SchemaRegistryClientSettings[IO]("http://localhost:8081")
      .withAuth(Auth.Basic("username", "password"))
  }; $doc.binder(avroSettings, 3, 4, 3, 16)
$doc.endStatement();
$doc.endSection();

$doc.startSection();
$doc.startStatement(0, 0, 0, 43);
import fs2.kafka.{Deserializer, Serializer}
$doc.endStatement();
$doc.startStatement(1, 0, 1, 58);
import fs2.kafka.vulcan.{avroDeserializer, avroSerializer}
$doc.endStatement();
$doc.startStatement(3, 0, 4, 44);
implicit val personSerializer: Serializer.Record[IO, Person] =
  avroSerializer[Person].using(avroSettings); $doc.binder(personSerializer, 3, 13, 3, 29)
$doc.endStatement();
$doc.startStatement(6, 0, 7, 46);
implicit val personDeserializer: Deserializer.Record[IO, Person] =
  avroDeserializer[Person].using(avroSettings); $doc.binder(personDeserializer, 6, 13, 6, 31)
$doc.endStatement();
$doc.endSection();

$doc.startSection();
$doc.startStatement(0, 0, 0, 70);
import fs2.kafka.{AutoOffsetReset, ConsumerSettings, ProducerSettings}
$doc.endStatement();
$doc.startStatement(2, 0, 6, 25);
val consumerSettings =
  ConsumerSettings[IO, String, Person]
    .withAutoOffsetReset(AutoOffsetReset.Earliest)
    .withBootstrapServers("localhost")
    .withGroupId("group"); $doc.binder(consumerSettings, 2, 4, 2, 20)
$doc.endStatement();
$doc.startStatement(8, 0, 10, 38);
val producerSettings =
  ProducerSettings[IO, String, Person]
    .withBootstrapServers("localhost"); $doc.binder(producerSettings, 8, 4, 8, 20)
$doc.endStatement();
$doc.endSection();

$doc.startSection();
$doc.startStatement(0, 0, 0, 43);
import fs2.kafka.{Deserializer, Serializer}
$doc.endStatement();
$doc.startStatement(2, 0, 7, 22);
val res1 = ConsumerSettings(
  keyDeserializer = Deserializer[IO, String],
  valueDeserializer = personDeserializer
).withAutoOffsetReset(AutoOffsetReset.Earliest)
 .withBootstrapServers("localhost")
 .withGroupId("group"); $doc.binder(res1, 2, 0, 7, 22)
$doc.endStatement();
$doc.startStatement(9, 0, 12, 35);
val res2 = ProducerSettings(
  keySerializer = Serializer[IO, String],
  valueSerializer = personSerializer
).withBootstrapServers("localhost"); $doc.binder(res2, 9, 0, 12, 35)
$doc.endStatement();
$doc.endSection();

$doc.startSection();
$doc.startStatement(0, 0, 4, 25);
val avroSettingsSharedClient: IO[AvroSettings[IO]] =
  SchemaRegistryClientSettings[IO]("http://localhost:8081")
    .withAuth(Auth.Basic("username", "password"))
    .createSchemaRegistryClient
    .map(AvroSettings(_)); $doc.binder(avroSettingsSharedClient, 0, 4, 0, 28)
$doc.endStatement();
$doc.endSection();

$doc.startSection();
$doc.startStatement(0, 0, 22, 1);
val res3 = avroSettingsSharedClient.map { avroSettings =>
  val personSerializer: Serializer.Record[IO, Person] =
    avroSerializer[Person].using(avroSettings)

  val personDeserializer: Deserializer.Record[IO, Person] =
    avroDeserializer[Person].using(avroSettings)

  val consumerSettings =
    ConsumerSettings(
      keyDeserializer = Deserializer[IO, String],
      valueDeserializer = personDeserializer
    ).withAutoOffsetReset(AutoOffsetReset.Earliest)
    .withBootstrapServers("localhost")
    .withGroupId("group")

 val producerSettings =
  ProducerSettings(
    keySerializer = Serializer[IO, String],
    valueSerializer = personSerializer
  ).withBootstrapServers("localhost")

  (consumerSettings, producerSettings)
}; $doc.binder(res3, 0, 0, 22, 1)
$doc.endStatement();
$doc.endSection();
  }
}

And following are all the warnings.

warning: modules.md:45 (mdoc generated code) method any2stringadd in object Predef is deprecated (since 2.13.0): Implicit injection of + is deprecated. Convert to String to call +
  }; $doc.binder(avroSettings, 3, 4, 3, 16)
                ^

warning: modules.md:94 (mdoc generated code) method any2stringadd in object Predef is deprecated (since 2.13.0): Implicit injection of + is deprecated. Convert to String to call +
 .withGroupId("group"); $doc.binder(res1, 2, 0, 7, 22)
                                   ^

warning: modules.md:100 (mdoc generated code) method any2stringadd in object Predef is deprecated (since 2.13.0): Implicit injection of + is deprecated. Convert to String to call +
).withBootstrapServers("localhost"); $doc.binder(res2, 9, 0, 12, 35)
                                                ^

warning: modules.md:138 (mdoc generated code) method any2stringadd in object Predef is deprecated (since 2.13.0): Implicit injection of + is deprecated. Convert to String to call +
}; $doc.binder(res3, 0, 0, 22, 1)
olafurpg commented 4 years ago

Interesting, the instrumented code doesn't seem to use +. The error seems to come from the sourcecode.Text.generate macro

https://github.com/scalameta/mdoc/blob/ae7613dcad4b39855a69e2c7139ca8e39dba52e3/runtime/src/main/scala/mdoc/internal/document/DocumentBuilder.scala#L38

I'm unable to minimize the issue however

$ cat foo.scala
object foo {
  def bar[T](e: sourcecode.Text[T]) = e.source
  val x = List(14)
  bar(x)
}
$  scalac -classpath $(cs fetch com.lihaoyi:sourcecode_2.12:0.1.8 -p) -Xprint:typer foo.scala
package <empty> {
  object foo extends scala.AnyRef {
    def <init>(): foo.type = {
      foo.super.<init>();
      ()
    };
    def bar[T](e: sourcecode.Text[T]): String = e.source;
    private[this] val x: List[Int] = scala.collection.immutable.List.apply[Int](14);
    <stable> <accessor> def x: List[Int] = foo.this.x;
    foo.this.bar[List[Int]]((sourcecode.this.Text.apply[List[Int]](foo.this.x, "x"): sourcecode.Text[List[Int]]))
  }
}

What happens if you add -Xprint:typer to the scalac options?

olafurpg commented 4 years ago

I can't see anything in the macro implementation itself why the + appears 🤔

https://github.com/lihaoyi/sourcecode/blob/c1f49c15e0366c489c275fe7e7ddcefac608b4b1/sourcecode/src/sourcecode/Macros.scala#L133-L149

olafurpg commented 4 years ago

OK, the + may be coming from the implicit pprint.TPrint[T] parameter

object foo {
  def bar[T](e: sourcecode.Text[T])(implicit tprint: pprint.TPrint[T]) = e.source + tprint.render
  val x = List(14)
  bar(x)
}

// ... -Xprint:typer

    foo.this.bar[List[Int]]((sourcecode.this.Text.apply[List[Int]](foo.this.x, "x"): sourcecode.Text[List[Int]]))((pprint.TPrint.lambda[List[Int]](((cfg$macro$1: pprint.TPrintColors) => "".+(cfg$macro$1.typeColor.apply(fansi.this.Str.implicitApply("List")).render).+("[".+(pprint.TPrint.implicitly[Int]((pprint.TPrint.lambda[Int](((cfg$macro$2: pprint.TPrintColors) => "".+(cfg$macro$2.typeColor.apply(fansi.this.Str.implicitApply("Int")).render).+(""))): pprint.TPrint[Int])).render(cfg$macro$1)).+("]")))): pprint.TPrint[List[Int]]))

I'm unable to reproduce the warning on 2.13.1 using List[Int] but you may be able to reproduce it with the type of avroSettings

cs launch scala:2.13.1 -M scala.tools.nsc.MainGenericRunner -- -classpath $(coursier fetch com.lihaoyi:pprint_2.13:0.5.6 -p) -deprecation foo.scala

If you can minimize the issue, then it might be worth reporting a bug to pprint

olafurpg commented 4 years ago

A workaround could be to drop -deprecation from scalacOptions

vlovgr commented 4 years ago

This indeed seems to happen due to use of + in PPrint. I'll try to minimize and replace uses of + in PPrint. Thanks a lot for having a look @olafurpg!

dgalichet commented 4 years ago

Hi, I'm reproducing this issue with a worksheet (with vscode + metals) and scala 2.13. I've got several warnings like this one:

.../sample.worksheet.sc:28 (mdoc generated code) method any2stringadd in object Predef is deprecated (since 2.13.0): Implicit injection of + is deprecated. Convert to String to call +
val response = io.unsafeRunSync(); $doc.binder(response, 13, 4, 13, 12)
olafurpg commented 3 years ago

Can you please confirm if you can reproduce with the latest version of mdoc? We recently inlined the parts of pprint in order to support Dotty, so this issue may be fixed now (or it would be easier to fix compared to before)

vlovgr commented 3 years ago

I'm still seeing these warnings with mdoc 2.2.13.

olafurpg commented 3 years ago

Thank you for confirming. It looks like pprint was only inlined for Scala 3, and pprint is still an external dependency on Scala 2.

tgodzik commented 3 years ago

Thank you for confirming. It looks like pprint was only inlined for Scala 3, and pprint is still an external dependency on Scala 2.

Actually, in Scala 3 we are currently using toString for values and code extracted from the compiler for types. So really, there is no part of pprint currently in mdoc, only sourcecode.

Kazark commented 3 years ago

I'm seeing it on sbt-mdoc 2.2.20 and Scala 2.13.4:

warning: index.md:122 (mdoc generated code) method any2stringadd in object Predef is deprecated (since 2.13.0): Implicit injection of + is deprecated. Convert to String to call + val avro4sProducer = producer.map(_.toAvro4s[CustomerId, Customer]); $doc.binder(avro4sProducer, 0, 4, 0, 18)

Kazark commented 3 years ago

Worked around it with scalacOptions += "-Wconf:cat=deprecation:i", which makes deprecation messages informational rather than warnings.