Repository: scalameta/scalameta
Pull request #3454 (2024-01-02 20:48:57)

Origin: move out of `internal`
As origin is now saved as part of quasiquotes, let's make sure it abides by MIMA standards (which are not currently applied to `internal` code).

Similarly, remove TokenStreamPosition and inline in Origin.Parsed. Helps with #3425.

kitbellew: @tgodzik @bjaglin i am having second thoughts about this. is my understanding even true? if origin is not a public field, does it really have to abide by any interface when quasiquotes are involved?
tgodzik: I think we use Origin in quite a few places outside of scalameta so it makes sense to move it out of internal.
bjaglin: (I see there is very active progress on this massive effort, thanks a lot for keeping backward compatibility in mind despite the complexity!)
kitbellew: @bjaglin thanks for chiming in. a further question:
- `internal.Impl.unapply` was problematic as that was the final result of this expansion;
- however, origin is "hidden" somewhere inside the tree and not directly exposed
  - after some thought, it looks to me no different from any other `internal` type, at least as far as it is used in the construction of a scalameta tree, so either all of them are bad or none are...
  - for instance, `InternalTree` is a base class of `Tree`, and yet it's  under`internal` package and hence outside of Mima
  - similarly, `.syntax` is implemented using `TreeSyntax` and that is also `internal`; so if you have code which uses this method, is it at risk if TreeSyntax changes?
bjaglin: I see your point, it's hard to tell what's really contractual at macro expansion time... Did you have a look at code generated by the quasiquotes macros with `-Vprint:typer`? I doubt that there is anything `internal` at the moment, but if there is, then I agree about your statement "either all of them are bad or none are".
kitbellew: > ... Did you have a look at code generated by the quasiquotes macros with `-Vprint:typer`?

i don't really know how to do that, truth be told... 😊

bjaglin: There is probably an easier way to do it, but what I did last time was to add `-Vprint:typer` to `scalacOptions` in a test module, run a full compile (ignoring the verbose output), touch a file exercising a macro by adding some dummy code, and check the output of that incremental compilation hunting for references.

And ... now that I write this, I realize `javap -p -v` on class files generated from macro-expanding sources is probably even easier to identify what really gets emitted and thus should be backward compatible. So that would be my recommendation!

Patch:
diff --git a/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/NamerMacros.scala b/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/NamerMacros.scala
index e41d37681..75f306476 100644
--- a/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/NamerMacros.scala
+++ b/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/NamerMacros.scala
@@ -24,8 +24,8 @@ trait CommonNamerMacros extends MacroHelpers {
   lazy val PositionModule = q"_root_.scala.meta.inputs.Position"
   lazy val PointClass = tq"_root_.scala.meta.inputs.Point"
   lazy val PointModule = q"_root_.scala.meta.inputs.Point"
-  lazy val OriginClass = tq"_root_.scala.meta.internal.trees.Origin"
-  lazy val OriginModule = q"_root_.scala.meta.internal.trees.Origin"
+  lazy val OriginClass = tq"_root_.scala.meta.trees.Origin"
+  lazy val OriginModule = q"_root_.scala.meta.trees.Origin"
 
   def mkClassifier(name: TypeName): List[Tree] = {
     val q"..$classifierBoilerplate" = q"""
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/Pos.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/Pos.scala
index 558f03108..ba6070106 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/Pos.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/Pos.scala
@@ -1,8 +1,8 @@
 package scala.meta.internal.parsers
 
 import scala.meta.Tree
-import scala.meta.internal.trees.Origin
 import scala.meta.prettyprinters._
+import scala.meta.trees.Origin
 
 // NOTE: `startTokenPos` and `endTokenPos` are BOTH INCLUSIVE.
 // This is at odds with the rest of scala.meta, where ends are non-inclusive.
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
index 226f992e5..627f67fb6 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
@@ -24,6 +24,7 @@ import scala.meta.prettyprinters._
 import scala.meta.tokenizers._
 import scala.meta.tokens._
 import scala.meta.tokens.Token._
+import scala.meta.trees.Origin
 
 import org.scalameta._
 import org.scalameta.invariants._
diff --git a/scalameta/quasiquotes/shared/src/main/scala/scala/meta/internal/quasiquotes/ReificationMacros.scala b/scalameta/quasiquotes/shared/src/main/scala/scala/meta/internal/quasiquotes/ReificationMacros.scala
index bdecd28a8..4c9ae0cee 100644
--- a/scalameta/quasiquotes/shared/src/main/scala/scala/meta/internal/quasiquotes/ReificationMacros.scala
+++ b/scalameta/quasiquotes/shared/src/main/scala/scala/meta/internal/quasiquotes/ReificationMacros.scala
@@ -19,6 +19,7 @@ import scala.meta.internal.parsers.Messages
 import scala.meta.internal.parsers.Absolutize._
 import scala.meta.parsers._
 import scala.meta.tokenizers._
+import scala.meta.trees.Origin
 
 class ReificationMacros(val c: Context) extends AstReflection with AdtLiftables with AstLiftables {
   lazy val u: c.universe.type = c.universe
@@ -357,12 +358,12 @@ class ReificationMacros(val c: Context) extends AstReflection with AdtLiftables
           pendingQuasis.pop()
         }
       }
-      private val originNone = q"_root_.scala.meta.internal.trees.Origin.None"
+      private val originNone = q"_root_.scala.meta.trees.Origin.None"
       val liftOrigin: Origin => ReflectTree =
         if (sourceName ne null)
           _ match {
             case Origin.Parsed(_, beg, end) =>
-              q"_root_.scala.meta.internal.trees.Origin.Parsed($sourceName, $beg, $end)"
+              q"_root_.scala.meta.trees.Origin.Parsed($sourceName, $beg, $end)"
             case _ => originNone
           }
         else _ => originNone
@@ -392,7 +393,7 @@ class ReificationMacros(val c: Context) extends AstReflection with AdtLiftables
       q"val $dialectName = $dialectTree",
       if (sourceName eq null) null
       else q"""
-        val $sourceName = new _root_.scala.meta.internal.trees.Origin.ParsedSource(
+        val $sourceName = new _root_.scala.meta.trees.Origin.ParsedSource(
           _root_.scala.meta.inputs.Input.String(${input.text})
         )($dialectName)
       """
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeSyntax.scala b/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeSyntax.scala
index 95145fbb0..8bb8169c2 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeSyntax.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeSyntax.scala
@@ -8,6 +8,7 @@ import scala.meta.internal.trees.{branch => _, root => _, _}
 import scala.meta.internal.tokenizers.Chars._
 import scala.meta.prettyprinters._
 import scala.meta.tokens.Token
+import scala.meta.trees.Origin
 
 import org.scalameta.adt._
 import org.scalameta.invariants._
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeToString.scala b/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeToString.scala
index 8d5c257b8..b5939be4d 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeToString.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeToString.scala
@@ -3,7 +3,8 @@ package internal
 package prettyprinters
 
 import scala.meta.dialects
-import scala.meta.internal.trees.{Quasi, Origin}
+import scala.meta.internal.trees.Quasi
+import scala.meta.trees.Origin
 
 object TreeToString {
   // this dialect is as good as any as a default
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/InternalTrees.scala b/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/InternalTrees.scala
index ca4918832..1d32e9628 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/InternalTrees.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/InternalTrees.scala
@@ -7,6 +7,7 @@ import scala.meta.prettyprinters._
 import scala.meta.tokenizers._
 import scala.meta.tokens._
 import scala.meta.tokens.Token._
+import scala.meta.trees.Origin
 
 // NOTE: Methods that start with "private" are NOT intended to be called outside scala.meta.
 // Calling these methods from hosts will compile (because hosts are in meta), but is strongly discouraged.
@@ -45,7 +46,7 @@ trait InternalTree extends Product {
   // def productIterator: Iterator[Any]
   def productFields: List[String]
 
-  private[meta] def origin: Origin = {
+  def origin: Origin = {
     val nullableOrigin = privateOrigin
     if (nullableOrigin != null) nullableOrigin else Origin.None
   }
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/Origin.scala b/scalameta/trees/shared/src/main/scala/scala/meta/trees/Origin.scala
similarity index 95%
rename from scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/Origin.scala
rename to scalameta/trees/shared/src/main/scala/scala/meta/trees/Origin.scala
index 4af9a5db2..fe6170ab7 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/Origin.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/trees/Origin.scala
@@ -1,9 +1,8 @@
-package scala.meta
-package internal
-package trees
+package scala.meta.trees
 
 import org.scalameta.adt
 
+import scala.meta.Dialect
 import scala.meta.common._
 import scala.meta.inputs._
 import scala.meta.tokenizers._


Test patch:
diff --git a/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala b/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala
index 2e8126f92..f07433330 100644
--- a/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala
+++ b/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala
@@ -172,6 +172,10 @@ class SurfaceSuite extends FunSuite {
       |scala.meta.transversers.Transformer
       |scala.meta.transversers.Traverser
       |scala.meta.trees
+      |scala.meta.trees.Origin *
+      |scala.meta.trees.Origin.None *
+      |scala.meta.trees.Origin.Parsed *
+      |scala.meta.trees.Origin.ParsedSource *
     """.trim.stripMargin.split('\n').mkString(EOL)
     )
   }
diff --git a/tests/jvm/src/test/scala-2.13/scala/meta/tests/testkit/prettyprinters/PrettyPrinterSuite.scala b/tests/jvm/src/test/scala-2.13/scala/meta/tests/testkit/prettyprinters/PrettyPrinterSuite.scala
index cc8886a45..d49ae6fcc 100644
--- a/tests/jvm/src/test/scala-2.13/scala/meta/tests/testkit/prettyprinters/PrettyPrinterSuite.scala
+++ b/tests/jvm/src/test/scala-2.13/scala/meta/tests/testkit/prettyprinters/PrettyPrinterSuite.scala
@@ -2,8 +2,8 @@ package scala.meta.tests.testkit
 package prettyprinters
 
 import scala.meta._
-import scala.meta.internal.trees.Origin
 import scala.meta.testkit.StructurallyEqual
+import scala.meta.trees.Origin
 
 import org.scalameta.logger
 import munit.FunSuite
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/ParseSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/ParseSuite.scala
index 0cd0839cd..7a6803dfe 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/ParseSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/ParseSuite.scala
@@ -4,7 +4,7 @@ package parsers
 import munit._
 import scala.meta._
 import scala.meta.internal.parsers._
-import scala.meta.internal.trees.Origin
+import scala.meta.trees.Origin
 import MoreHelpers._
 
 import org.scalameta.logger
diff --git a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala
index 85efb1042..f2c62c585 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala
@@ -444,4 +444,9 @@ class PublicSuite extends TreeSuiteBase {
   test("scala.meta.XtensionDialectTreeSyntax") {}
   test("scala.meta.XtensionDialectTokensSyntax") {}
 
+  test("scala.meta.trees.Origin") {}
+  test("scala.meta.trees.Origin.None") {}
+  test("scala.meta.trees.Origin.Parsed") {}
+  test("scala.meta.trees.Origin.ParsedSource") {}
+
 }
diff --git a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/SyntacticSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/SyntacticSuite.scala
index dd1f3f67f..f997d1e54 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/SyntacticSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/SyntacticSuite.scala
@@ -2,8 +2,8 @@ package scala.meta.tests
 package prettyprinters
 
 import scala.meta._
-import scala.meta.internal.trees.Origin
 import scala.meta.prettyprinters.Show
+import scala.meta.trees.Origin
 
 class SyntacticSuite extends scala.meta.tests.parsers.ParseSuite {
   override def term(code: String)(implicit dialect: Dialect) =
diff --git a/tests/shared/src/test/scala/scala/meta/tests/quasiquotes/SuccessSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/quasiquotes/SuccessSuite.scala
index 6a847cf1e..9f5408e38 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/quasiquotes/SuccessSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/quasiquotes/SuccessSuite.scala
@@ -6,7 +6,7 @@ import org.scalameta.invariants.InvariantFailedException
 
 import scala.meta._
 import scala.meta.dialects.Scala211
-import scala.meta.internal.trees.Origin
+import scala.meta.trees.Origin
 
 import compat.Platform.EOL