diff --git a/_overviews/scala3-migration/tutorial-macro-cross-building.md b/_overviews/scala3-migration/tutorial-macro-cross-building.md index bcf2462768..6a27f616b1 100644 --- a/_overviews/scala3-migration/tutorial-macro-cross-building.md +++ b/_overviews/scala3-migration/tutorial-macro-cross-building.md @@ -31,6 +31,8 @@ lazy val example = project ) ``` +{% tabs scala-2-location %} +{% tab 'Scala 2 Only' %} ```scala // example/src/main/scala/location/Location.scala package location @@ -52,6 +54,8 @@ object Macros { } } ``` +{% endtab %} +{% endtabs %} You should recognize some similarities with your library: one or more macro methods, in our case the `location` method, are implemented by consuming a macro `Context` and returning a `Tree` from this context. @@ -116,13 +120,19 @@ All the code that cannot be compiled by the Scala 3 compiler goes to the `src/ma In our example, the `Location` class stays in the `src/main/scala` folder but the `Macros` object is moved to the `src/main/scala-2` folder: +{% tabs shared-location %} +{% tab 'Scala 2 and 3' %} ```scala // example/src/main/scala/location/Location.scala package location case class Location(path: String, line: Int) ``` +{% endtab %} +{% endtabs %} +{% tabs scala-2-location_2 %} +{% tab 'Scala 2 Only' %} ```scala // example/src/main/scala-2/location/Macros.scala package location @@ -142,10 +152,14 @@ object Macros { } } ``` +{% endtab %} +{% endtabs %} Now we can initialize each of our Scala 3 macro definitions in the `src/main/scala-3` folder. They must have the exact same signature than their Scala 2.13 counterparts. +{% tabs scala-3-location_1 %} +{% tab 'Scala 3 Only' %} ```scala // example/src/main/scala-3/location/Macros.scala package location @@ -153,6 +167,8 @@ package location object Macros: def location: Location = ??? ``` +{% endtab %} +{% endtabs %} ## 3. Implement the Scala 3 macro @@ -161,6 +177,8 @@ One needs to learn about the new [Metaprogramming](compatibility-metaprogramming We eventually come up with this implementation: +{% tabs scala-3-location_2 %} +{% tab 'Scala 3 Only' %} ```scala // example/src/main/scala-3/location/Macros.scala package location @@ -177,6 +195,8 @@ object Macros: val line = Expr(pos.startLine + 1) '{new Location($file, $line)} ``` +{% endtab %} +{% endtabs %} ## 4. Cross-validate the macro @@ -184,6 +204,8 @@ Adding some tests is important to check that the macro method works the same in In our example, we add a single test. +{% tabs shared-test %} +{% tab 'Scala 2 and 3' %} ```scala // example/src/test/scala/location/MacrosSpec.scala package location @@ -194,6 +216,8 @@ class MacrosSpec extends munit.FunSuite { } } ``` +{% endtab %} +{% endtabs %} You should now be able to run the tests in both versions. diff --git a/_overviews/scala3-migration/tutorial-macro-mixing.md b/_overviews/scala3-migration/tutorial-macro-mixing.md index fbd3b907a9..10be2418a6 100644 --- a/_overviews/scala3-migration/tutorial-macro-mixing.md +++ b/_overviews/scala3-migration/tutorial-macro-mixing.md @@ -21,6 +21,8 @@ This is only possible in Scala 3, since the Scala 3 compiler can read both the S Let's start by considering the following code skeleton: +{% tabs scala-3-location_1 %} +{% tab 'Scala 3 Only' %} ```scala // example/src/main/scala/location/Location.scala package location @@ -31,6 +33,8 @@ object Macros: def location: Location = macro ??? inline def location: Location = ${ ??? } ``` +{% endtab %} +{% endtabs %} As you can see the `location` macro is defined twice: - `def location: Location = macro ???` is a Scala 2.13 macro definition @@ -46,6 +50,8 @@ The explanation is that it recognizes the first definition is for Scala 2.13 onl You can put the Scala 3 macro implementation alongside the definition. +{% tabs scala-3-location_2 %} +{% tab 'Scala 3 Only' %} ```scala package location @@ -63,12 +69,17 @@ object Macros: val line = Expr(Position.ofMacroExpansion.startLine + 1) '{new Location($file, $line)} ``` +{% endtab %} +{% endtabs %} ## 2. Implement the Scala 2 macro The Scala 3 compiler can compile a Scala 2 macro implementation if it contains no quasiquote or reification. For instance this piece of code does compile with Scala 3, and so you can put it alongside the Scala 3 implementation. + +{% tabs scala-2-and-3-location %} +{% tab 'Scala 2 and 3' %} ```scala import scala.reflect.macros.blackbox.Context @@ -79,6 +90,8 @@ def locationImpl(c: Context): c.Tree = { New(c.mirror.staticClass(classOf[Location].getName()), path, line) } ``` +{% endtab %} +{% endtabs %} However, in many cases you will have to move the Scala 2.13 macro implementation in a Scala 2.13 submodule. @@ -102,6 +115,8 @@ Here `example`, our main library compiled in Scala 3, depends on `example-compat In such a case we can put the Scala 2 macro implementation in `example-compat` and use quasiquotes. +{% tabs scala-2-location %} +{% tab 'Scala 2 Only' %} ```scala package location @@ -120,6 +135,8 @@ object Scala2MacrosCompat { } } ``` +{% endtab %} +{% endtabs %} Note that we had to move the `Location` class downstream. @@ -149,6 +166,9 @@ lazy val `example-test` = project.in(file("example-test")) > `-Ytasty-reader` is needed in Scala 2.13 to consume Scala 3 artifacts For instance the test can be: + +{% tabs scala-2-and-3-test %} +{% tab 'Scala 2 and 3' %} ```scala // example-test/src/test/scala/location/MacrosSpec.scala package location @@ -159,6 +179,8 @@ class MacrosSpec extends munit.FunSuite { } } ``` +{% endtab %} +{% endtabs %} You should now be able to run the tests in both versions.