Repository: scalameta/scalameta
Pull request #3445 (2024-01-02 09:09:02)

Origin: extract input/dialect into separate class
When we implement lifting of origin in quasiquotes, it would make the resulting code more compact if the common part (input and dialect) are lifted only once, rather than for every subtree, thus leaving only the position field to change.

Helps with #3425.


Patch:
diff --git a/scalameta/common/shared/src/main/scala/org/scalameta/adt/Reflection.scala b/scalameta/common/shared/src/main/scala/org/scalameta/adt/Reflection.scala
index 8d3a75502..c01397d9b 100644
--- a/scalameta/common/shared/src/main/scala/org/scalameta/adt/Reflection.scala
+++ b/scalameta/common/shared/src/main/scala/org/scalameta/adt/Reflection.scala
@@ -1,9 +1,10 @@
 package org.scalameta.adt
 
-import scala.reflect.api.Universe
 import org.scalameta.adt.{Metadata => AdtMetadata}
 import scala.meta.internal.trees.{Metadata => AstMetadata}
-import scala.reflect.{classTag, ClassTag}
+
+import scala.reflect.{ClassTag, classTag}
+import scala.reflect.api.Universe
 
 trait Reflection {
   val u: Universe
@@ -35,16 +36,14 @@ trait Reflection {
     def isRoot: Boolean = sym.hasAnnotation[AdtMetadata.root]
     def isBranch: Boolean = sym.hasAnnotation[AdtMetadata.branch]
     def isLeaf: Boolean = sym.hasAnnotation[AdtMetadata.leafClass]
-    def isField: Boolean = {
-      val isMethodInLeafClass = sym.isMethod && sym.owner.isLeaf
-      val isParamGetter =
-        sym.isTerm && sym.asTerm.isParamAccessor && sym.asTerm.isGetter && sym.isPublic
-      val isAstField = sym.hasAnnotation[AstMetadata.astField]
-      isMethodInLeafClass && (isParamGetter || isAstField)
+    def isField: Boolean = sym match {
+      case m: MethodSymbolApi if m.owner.isLeaf =>
+        sym.hasAnnotation[AstMetadata.astField] ||
+        m.isPublic && m.isParamAccessor && m.isGetter
+      case _ => false
     }
     def isPayload: Boolean = sym.isField && !sym.isAuxiliary
     def isAuxiliary: Boolean = sym.isField && sym.hasAnnotation[AstMetadata.auxiliary]
-    def isByNeed: Boolean = sym.isField && sym.hasAnnotation[AdtMetadata.byNeedField]
     def asAdt: Adt =
       if (isRoot) sym.asRoot
       else if (isBranch) sym.asBranch
@@ -77,7 +76,7 @@ trait Reflection {
 
     def root: Symbol = sym.asClass.baseClasses.reverse.find(_.isRoot).getOrElse(NoSymbol)
     def fields: List[Symbol] = allFields.filter(p => p.isPayload)
-    def allFields: List[Symbol] = sym.info.decls.filter(_.isField).toList
+    def allFields: List[Symbol] = sym.info.decls.sorted.filter(_.isField)
   }
 
   abstract class Adt(val sym: Symbol) {
@@ -126,11 +125,8 @@ trait Reflection {
     def owner: Leaf = sym.owner.asLeaf
     def name: TermName = TermName(sym.name.toString.stripPrefix("_"))
     def tpe: Type = sym.info.finalResultType
-    def isPayload: Boolean = sym.isPayload
-    def isAuxiliary: Boolean = sym.isAuxiliary
-    def isByNeed: Boolean = sym.isByNeed
     override def toString =
-      s"field ${owner.prefix}.$name: $tpe" + (if (isAuxiliary) " (auxiliary)" else "")
+      s"field ${owner.prefix}.$name: $tpe" + (if (sym.isAuxiliary) " (auxiliary)" else "")
   }
 
   private def isExemptParentSymbol(bsym: ClassSymbol): Boolean = {
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 a5aec1d4d..e41d37681 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
@@ -116,4 +116,26 @@ trait CommonNamerMacros extends MacroHelpers {
       }
     """
   }
+
+  protected case class PrivateField(field: ValOrDefDef)
+  protected case class PrivateFields(
+      prototype: PrivateField,
+      parent: PrivateField,
+      origin: PrivateField
+  ) {
+    def asList = prototype :: parent :: origin :: Nil
+  }
+
+  protected def getPrivateFields(iname: TypeName): PrivateFields = PrivateFields(
+    PrivateField(
+      q"@$TransientAnnotation private[meta] override val privatePrototype: $iname = null"
+    ),
+    PrivateField(
+      q"private[meta] override val privateParent: $TreeClass = null"
+    ),
+    PrivateField(
+      q"private[meta] override val privateOrigin: $OriginClass = $OriginModule.None"
+    )
+  )
+
 }
diff --git a/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/ast.scala b/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/ast.scala
index d84679b3c..c86d970ee 100644
--- a/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/ast.scala
+++ b/scalameta/common/shared/src/main/scala/scala/meta/internal/trees/ast.scala
@@ -122,11 +122,9 @@ class AstNamerMacros(val c: Context) extends Reflection with CommonNamerMacros {
         stats1 ++= quasiExtraAbstractDefs
 
         // step 4: implement the unimplemented methods in InternalTree (part 1)
-        val bparams1 = List(
-          q"@$TransientAnnotation private[meta] val privatePrototype: $iname",
-          q"private[meta] val privateParent: $TreeClass",
-          q"private[meta] val privateOrigin: $OriginClass"
-        )
+        val privateFields = getPrivateFields(iname)
+        val privateParams = privateFields.asList
+        val bparams1 = privateParams.map(_.field)
 
         // step 5: turn all parameters into vars, create getters and setters
         params.foreach { p =>
@@ -171,9 +169,9 @@ class AstNamerMacros(val c: Context) extends Reflection with CommonNamerMacros {
         stats1 += q"""
         private[meta] def privateCopy(
             prototype: $TreeClass = this,
-            parent: $TreeClass = privateParent,
+            parent: $TreeClass = ${privateFields.parent.field.name},
             destination: $StringClass = null,
-            origin: $OriginClass = privateOrigin): Tree = {
+            origin: $OriginClass = ${privateFields.origin.field.name}): Tree = {
           $privateCopyParentChecks
           new $name(prototype.asInstanceOf[$iname], parent, origin)(..$privateCopyArgs)
         }
@@ -185,7 +183,7 @@ class AstNamerMacros(val c: Context) extends Reflection with CommonNamerMacros {
         // NOTE: Can't generate XXX.Quasi.copy, because XXX.Quasi already inherits XXX.copy,
         // and there can't be multiple overloaded methods with default parameters.
         // Not a big deal though, since XXX.Quasi is an internal class.
-        def getParamArg(p: ValDef): TermName = p.name
+        def getParamArg(p: ValOrDefDef) = q"${p.name}"
         def addCopy(params: List[ValDef], annots: Tree*) = {
           val mods = getDeferredModifiers(annots.toList)
           istats1 += q"""
@@ -324,12 +322,19 @@ class AstNamerMacros(val c: Context) extends Reflection with CommonNamerMacros {
           errorChecker.traverse(fieldCheck)
           if (!hasErrors) internalBody += fieldCheck
         }
-        val internalInitCount = 3 // privatePrototype, privateParent, privateOrigin
-        val internalInitss = 1.to(internalInitCount).map(_ => q"null")
         val paramInits = params.map { p =>
           q"$CommonTyperMacrosModule.initParam(${p.name})"
         }
-        internalBody += q"val node = new $name(..$internalInitss)(..$paramInits)"
+        privateParams.foreach { p =>
+          internalBody += asValDefn(p.field)
+        }
+        internalBody += q"""
+          val node = new $name(
+            ..${bparams1.map(getParamArg)}
+          )(
+            ..$paramInits
+          )
+        """
         params.foreach(p => internalBody += storeField(p))
         internalBody += q"node"
         val applyParams =
@@ -357,10 +362,20 @@ class AstNamerMacros(val c: Context) extends Reflection with CommonNamerMacros {
             defn.foreach(applyBodyBuilder += _)
           }
           val params = positionVersionedParams(applyParamsBuilder.result())
-          applyBodyBuilder += q"$mname.apply(..$internalArgs)"
           val applyBody = applyBodyBuilder.result()
-          verMstats += q"def apply(..$params): $iname = { ..$applyBody }"
-          mstats1 += q"@${getDeprecatedAnno(v)} def apply(..$params): $iname = { ..$applyBody }"
+          val applyCall = q"$mname.apply(..$internalArgs)"
+          verMstats += q"""
+            def apply(..$params): $iname = {
+              ..$applyBody
+              $applyCall
+            }
+          """
+          mstats1 += q"""
+            @${getDeprecatedAnno(v)} def apply(..$params): $iname = {
+              ..$applyBody
+              $applyCall
+            }
+          """
         }
 
         // step 14: generate Companion.unapply
diff --git a/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala b/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
index 717a17212..bf1d9677e 100644
--- a/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
+++ b/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
@@ -7,7 +7,7 @@ import scala.collection.immutable.TreeMap
 /**
  * A dialect is used to configure what Scala syntax is allowed during tokenization and parsing.
  */
-final class Dialect private (
+final class Dialect private[meta] (
     // Are `&` intersection types supported by this dialect?
     @deprecated("allowAndTypes unneeded, infix types are supported", "4.5.1")
     val allowAndTypes: Boolean,
@@ -636,9 +636,13 @@ final class Dialect private (
     Dialect.inverseStandards.getOrElse(this, "Dialect()")
   }
 
-  def isEquivalentTo(that: Dialect): Boolean = (
-    // do not include deprecated values in this comparison
-    this.allowAtForExtractorVarargs == that.allowAtForExtractorVarargs
+  def isEquivalentTo(that: Dialect): Boolean =
+    isEquivalentTo(that, ignoreQuasiquotes = false)
+
+  def isEquivalentTo(that: Dialect, ignoreQuasiquotes: Boolean): Boolean = (
+    (this eq that) ||
+      // do not include deprecated values in this comparison
+      this.allowAtForExtractorVarargs == that.allowAtForExtractorVarargs
       && this.allowCaseClassWithoutParameterList == that.allowCaseClassWithoutParameterList
       && this.allowColonForExtractorVarargs == that.allowColonForExtractorVarargs
       && this.allowEnums == that.allowEnums
@@ -646,14 +650,14 @@ final class Dialect private (
       && this.allowInlineIdents == that.allowInlineIdents
       && this.allowInlineMods == that.allowInlineMods
       && this.allowLiteralTypes == that.allowLiteralTypes
-      && this.allowMultilinePrograms == that.allowMultilinePrograms
-      && this.allowPatUnquotes == that.allowPatUnquotes
+      && (ignoreQuasiquotes || this.allowMultilinePrograms == that.allowMultilinePrograms)
+      && (ignoreQuasiquotes || this.allowPatUnquotes == that.allowPatUnquotes)
       && this.allowSpliceUnderscores == that.allowSpliceUnderscores
-      && this.allowTermUnquotes == that.allowTermUnquotes
+      && (ignoreQuasiquotes || this.allowTermUnquotes == that.allowTermUnquotes)
       && this.allowToplevelTerms == that.allowToplevelTerms
       && this.allowTrailingCommas == that.allowTrailingCommas
       && this.allowTraitParameters == that.allowTraitParameters
-      && this.allowTypeLambdas == that.allowTypeLambdas
+      && (ignoreQuasiquotes || this.allowTypeLambdas == that.allowTypeLambdas)
       && this.allowViewBounds == that.allowViewBounds
       && this.allowXmlLiterals == that.allowXmlLiterals
       && this.allowNumericLiteralUnderscoreSeparators == that.allowNumericLiteralUnderscoreSeparators
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 72ded25d7..9733b7743 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
@@ -23,7 +23,9 @@ class IndexPos(index: => Int) extends Pos {
 
 class TreePos(tree: Tree) extends Pos {
   val (startTokenPos, endTokenPos) = tree.origin match {
-    case Origin.Parsed(_, _, pos) => (pos.start, pos.end - 1)
+    case x: Origin.Parsed =>
+      val pos = x.pos
+      (pos.start, pos.end - 1)
     case _ =>
       sys.error(s"internal error: unpositioned prototype ${tree.syntax}: ${tree.structure}")
   }
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 982a9e170..7a0197010 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
@@ -341,6 +341,8 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     finally next()
   }
 
+  private val originSource = new Origin.ParsedSource(input)
+
   def atPosWithBody[T <: Tree](startPos: Int, body: T, endPos: Int): T = {
     @tailrec def getStart(pos: Int): Int =
       if (pos > endPos) startPos else if (tokens(pos).is[Trivia]) getStart(pos + 1) else pos
@@ -350,7 +352,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     val end = if (endPos < startPos) startPos - 1 else getEnd(endPos)
     val endExcl = if (start == end && tokens(start).is[Trivia]) end else end + 1
     val pos = TokenStreamPosition(start, endExcl)
-    body.withOrigin(Origin.Parsed(input, dialect, pos))
+    body.withOrigin(Origin.Parsed(originSource, pos))
   }
 
   def atPosTry[T <: Tree](start: StartPos, end: EndPos)(body: => Try[T]): Try[T] = {
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 4f65b7c77..62dc27e13 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
@@ -33,7 +33,7 @@ class ReificationMacros(val c: Context) extends AstReflection with AdtLiftables
 
   // NOTE: only Mode.Pattern really needs holes, and that's only because of Scala's limitations
   // read a comment in liftUnquote for more information on that
-  case class Hole(name: TermName, arg: ReflectTree, var reifier: ReflectTree)
+  case class Hole(arg: ReflectTree, idx: Int, var reifier: ReflectTree = EmptyTree)
   sealed trait Mode {
     def isTerm: Boolean = this.isInstanceOf[Mode.Term]
     def multiline: Boolean
@@ -59,28 +59,33 @@ class ReificationMacros(val c: Context) extends AstReflection with AdtLiftables
     reifySkeleton(skeleton, mode)
   }
 
+  private def mkHoles(args: Seq[ReflectTree]) = {
+    val res = List.newBuilder[Hole]
+    var i = 0
+    args.foreach { arg =>
+      res += Hole(arg, i)
+      i += 1
+    }
+    res.result()
+  }
+
   private def extractQuasiquotee(): (Input, Mode) = {
     val reflectInput = c.macroApplication.pos.source
+    def isMultiline(firstPart: ReflectTree) = {
+      reflectInput.content(firstPart.pos.start - 2) == '"'
+    }
     val (parts, mode) = {
-      def isMultiline(firstPart: ReflectTree) = {
-        reflectInput.content(firstPart.pos.start - 2) == '"'
-      }
-      def mkHole(argi: (ReflectTree, Int)) = {
-        val (arg, i) = argi
-        val name = TermName(QuasiquotePrefix + "$hole$" + i)
-        Hole(name, arg, reifier = EmptyTree)
-      }
       try {
         c.macroApplication match {
           case q"$_($_.apply(..$parts)).$_.apply[..$_](..$args)($_)" =>
             require(parts.length == args.length + 1)
-            val holes = args.zipWithIndex.map(mkHole)
+            val holes = mkHoles(args)
             (parts, Mode.Term(isMultiline(parts.head), holes))
           case q"$_($_.apply(..$parts)).$_.unapply[..$_](${unapplySelector: Ident})($_)" =>
             require(unapplySelector.name == TermName("<unapply-selector>"))
             val args = c.internal.subpatterns(unapplySelector).get
             require(parts.length == args.length + 1)
-            val holes = args.zipWithIndex.map(mkHole)
+            val holes = mkHoles(args)
             (parts, Mode.Pattern(isMultiline(parts.head), holes, unapplySelector))
         }
       } catch {
@@ -96,16 +101,16 @@ class ReificationMacros(val c: Context) extends AstReflection with AdtLiftables
       val start = firstPart.pos.start // looks like we can trust this position to point to the character right after the opening quotes
       val end = { // we have to infer this for ourselves, because there's no guarantee we run under -Yrangepos
         var remaining = s_lastpart.length
-        var curr = lastPart.pos.start - 1
+        var curr = lastPart.pos.start
         while (remaining > 0) {
-          curr += 1
           if (reflectInput.content(curr) == '$') {
             curr += 1
             require(reflectInput.content(curr) == '$')
           }
+          curr += 1
           remaining -= 1
         }
-        curr + 1
+        curr
       }
       val metaInput = Input.VirtualFile(reflectInput.path, new String(reflectInput.content))
       Input.Slice(metaInput, start, end)
@@ -326,9 +331,9 @@ class ReificationMacros(val c: Context) extends AstReflection with AdtLiftables
                   case pq"$_: $explicitPt" => explicitPt
                   case _ => TypeTree(inferredPt)
                 }
-                hole.reifier =
-                  atPos(quasi.pos)(q"$InternalUnlift.unapply[$unliftedPt](${hole.name})")
-                pq"${hole.name}"
+                val name = TermName(QuasiquotePrefix + "$hole$" + hole.idx)
+                hole.reifier = atPos(quasi.pos)(q"$InternalUnlift.unapply[$unliftedPt]($name)")
+                pq"$name"
             }
             atPos(quasi.pos)(lifted)
           } else {
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 214b25f83..6c2f5e440 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
@@ -1136,7 +1136,7 @@ object TreeSyntax {
         // NOTE: Options don't really matter,
         // because if we've parsed a tree, it's not gonna contain lazy seqs anyway.
         // case Origin.Parsed(_, originalDialect, _) if dialect == originalDialect && options == Options.Eager =>
-        case o @ Origin.Parsed(_, `dialect`, _) => s(o.position.text)
+        case o: Origin.Parsed if o.dialect eq dialect => s(o.position.text)
         case _ => reprint(x)(dialect)
       }
     }
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 20464c40c..65cdcb673 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
@@ -35,9 +35,7 @@ trait InternalTree extends Product {
   // Getters for pieces of internal state defined above.
   // ==============================================================
 
-  def parent: Option[Tree] = {
-    if (privateParent != null) scala.Some(privateParent) else None
-  }
+  def parent: Option[Tree] = Option(privateParent)
 
   // NOTE: InternalTree inherits traditional productXXX methods from Product
   // and also adds a new method called productFields.
@@ -48,15 +46,11 @@ trait InternalTree extends Product {
   def productFields: List[String]
 
   private[meta] def origin: Origin = {
-    if (privateOrigin != null) privateOrigin else Origin.None
+    val nullableOrigin = privateOrigin
+    if (nullableOrigin != null) nullableOrigin else Origin.None
   }
 
-  def pos: Position = {
-    origin match {
-      case x: Origin.Parsed => x.position
-      case _ => Position.None
-    }
-  }
+  def pos: Position = origin.position
 
   def tokens(implicit dialect: Dialect): Tokens = {
     origin match {
@@ -93,7 +87,6 @@ trait InternalTree extends Product {
 
 trait InternalTreeXtensions {
   private[meta] implicit class XtensionOriginTree[T <: Tree](tree: T) {
-    def origin: Origin = if (tree.privateOrigin != null) tree.privateOrigin else Origin.None
     def withOrigin(origin: Origin): T = tree.privateWithOrigin(origin).asInstanceOf[T]
   }
 }
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/Origin.scala b/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/Origin.scala
index 57f2588fc..f75108825 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/Origin.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/Origin.scala
@@ -6,18 +6,23 @@ import org.scalameta.adt
 import scala.meta.common._
 import scala.meta.inputs._
 import scala.meta.internal.tokens._
-import scala.meta.tokens._
 import scala.meta.tokenizers._
+import scala.meta.tokens._
 
 @adt.root
-trait Origin extends Optional
+trait Origin extends Optional {
+  def position: Position
+}
+
 object Origin {
   @adt.none
-  object None extends Origin
+  object None extends Origin {
+    val position: Position = Position.None
+  }
 
   @adt.leaf
-  class Parsed(input: Input, dialect: Dialect, pos: TokenStreamPosition) extends Origin {
-    @inline private def tokenize() = dialect(input).tokenize.get
+  class Parsed(source: ParsedSource, pos: TokenStreamPosition) extends Origin {
+    @inline private def tokenize() = source.tokens
 
     def position: Position = {
       val tokens = tokenize()
@@ -29,6 +34,14 @@ object Origin {
     def tokens: Tokens = {
       tokenize().slice(pos.start, pos.end)
     }
+
+    @inline def input: Input = source.input
+    @inline def dialect: Dialect = source.dialect
+  }
+
+  class ParsedSource(val input: Input)(implicit val dialect: Dialect) {
+    lazy val tokenized = implicitly[Tokenize].apply(input, dialect)
+    @inline def tokens = tokenized.get
   }
 
 }


Test patch:
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 957921513..2cb2f0572 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
@@ -1823,7 +1823,8 @@ class SyntacticSuite extends scala.meta.tests.parsers.ParseSuite {
     val syntax = "('\\n', '\\n')"
     val tree = super.term(expr)
     val charN = Lit.Char('\n')
-    val origin = Origin.Parsed(Input.String(exprU), implicitly[Dialect], TokenStreamPosition(1, 2))
+    val originSource = new Origin.ParsedSource(Input.String(exprU))
+    val origin = Origin.Parsed(originSource, TokenStreamPosition(1, 2))
     val charU = charN.withOrigin(origin)
     checkTree(tree, expr) {
       Term.Tuple(List(charN, charU))
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 22c5c3da0..d391a28f0 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
@@ -3032,7 +3032,7 @@ class SuccessSuite extends TreeSuiteBase {
   test("#3409") {
     val code: Tree = source"object Generated {}"
     code.privateOrigin match {
-      case null =>
+      case Origin.None =>
       case x => fail(s"origin doesn't match: $x")
     }
   }