Repository: scalameta/scalameta
Pull request #2566 (2022-01-27 22:30:07)

ScalametaParser: Avoid stack overflow when annotation comes after modifier
Before this PR, parsing ```implicit @foo def foo(): Int``` would stack overflow. This is because `tmplDef` is happy to accept modifiers, even though in all cases it's been used, modifiers have already been parsed. This seems to only be a problem for dotty, but I added a test for both anyways. 


Patch:
diff --git a/.scalafmt.conf b/.scalafmt.conf
index a096f64d2..5c80ece00 100644
--- a/.scalafmt.conf
+++ b/.scalafmt.conf
@@ -1,4 +1,4 @@
-version = 3.3.1
+version = 2.7.5
 align.preset = none
 align.stripMargin = true
 assumeStandardLibraryStripMargin = true
diff --git a/build.sbt b/build.sbt
index 736936c86..df19b5337 100644
--- a/build.sbt
+++ b/build.sbt
@@ -317,7 +317,7 @@ lazy val testkit = crossProject(JSPlatform, JVMPlatform, NativePlatform)
     hasLargeIntegrationTests,
     libraryDependencies ++= Seq(
       "org.scalameta" %%% "munit" % munitVersion,
-      "com.lihaoyi" %%% "geny" % "0.7.0"
+      "com.lihaoyi" %%% "geny" % "0.6.11"
     ),
     testFrameworks := List(new TestFramework("munit.Framework")),
     description := "Testing utilities for scalameta APIs"
diff --git a/project/build.properties b/project/build.properties
index e64c208ff..baf5ff3ec 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1,1 +1,1 @@
-sbt.version=1.5.8
+sbt.version=1.5.7
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 43d349c1b..9f7e38ea3 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -6,7 +6,7 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3")
 
 addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.10.0")
 
-addSbtPlugin("com.thesamet" % "sbt-protoc" % "1.0.6")
+addSbtPlugin("com.thesamet" % "sbt-protoc" % "1.0.5")
 libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.11.4"
 
 libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value
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 b2e7d9d0a..50b516c39 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
@@ -38,24 +38,13 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
 
   /* ------------- PARSER ENTRY POINTS -------------------------------------------- */
 
-  /** Utility for tracking a context, like whether we are inside a pattern or a quote. */
-  private trait NestedContextBase {
+  private object QuoteSpliceContext {
     private var nested = 0
-    def within[T](body: => T): T = {
-      nested += 1
-      try {
-        body
-      } finally {
-        nested -= 1
-      }
-    }
+    def enter() = nested += 1
+    def exit() = nested -= 1
     def isInside() = nested > 0
   }
 
-  private object QuoteSpliceContext extends NestedContextBase
-
-  private object QuotedPatternContext extends NestedContextBase
-
   def parseRule[T <: Tree](rule: this.type => T): T = {
     // NOTE: can't require in.tokenPos to be at -1, because TokIterator auto-rewinds when created
     // require(in.tokenPos == -1 && debug(in.tokenPos))
@@ -389,41 +378,13 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           case region :: tail if region.isIndented && canEndIndentation(curr.token) =>
             sepRegions = tail
             val outdent = mkOutdentToken(curr.token)
-            val outdentPos = findOutdentPos(prevPos, curr.pos, region)
-            curr = TokenRef(outdent, curr.pos, curr.pos, outdentPos)
+            curr = TokenRef(outdent, curr.pos, curr.pos, prevPos)
             true
           case _ => false
         }
       }
     }
 
-    private def findOutdentPos(prevPos: Int, currPos: Int, region: SepRegion): Int = {
-      val outdent = region.indent
-      @tailrec
-      def iter(i: Int, pos: Int, indent: Int): Int = {
-        if (i >= currPos) {
-          if (pos < currPos) pos else currPos - 1
-        } else {
-          scannerTokens(i) match {
-            case _: LF | _: FF =>
-              iter(i + 1, if (pos == prevPos) i else pos, 0)
-            case _: Space | _: Tab if indent >= 0 =>
-              iter(i + 1, pos, indent + 1)
-            case Whitespace() =>
-              iter(i + 1, pos, indent)
-            case _: Comment if indent < 0 || outdent <= indent =>
-              iter(i + 1, i + 1, -1)
-            case _ => pos
-          }
-        }
-      }
-
-      val iterPos = 1 + prevPos
-      if (iterPos < currPos) iter(iterPos, prevPos, -1)
-      else if (scannerTokens(currPos).is[EOF]) currPos
-      else prevPos
-    }
-
     @tailrec
     private def nextToken(
         prevPos: Int,
@@ -449,38 +410,27 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       def mkIndent(pointPos: Int): TokenRef =
         TokenRef(mkIndentToken(curr), prevPos, currPos, pointPos)
 
-      def mkOutdent(region: SepRegion): TokenRef =
-        mkOutdentTo(region, currPos)
-
-      def mkOutdentTo(region: SepRegion, maxPointPos: Int): TokenRef = {
-        val pointPos = findOutdentPos(prevPos, maxPointPos, region)
+      def mkOutdent(pointPos: Int): TokenRef =
         TokenRef(mkOutdentToken(curr), prevPos, currPos, pointPos)
-      }
 
       def currRef: TokenRef = TokenRef(curr, currPos, currPos + 1, currPos)
 
-      def indentationWithinParenRegion: Option[SepRegion] = {
-        def isWithinParenRegion =
-          sepRegions.tail
+      def indentationWithinParenRegion: Boolean =
+        sepRegions.headOption.exists(!_.isInstanceOf[RegionParen]) &&
+          sepRegions
             .collectFirst {
               case RegionParen(_) => true
               case other if !other.isIndented => false
             }
-            .contains(true)
-        sepRegions.headOption.filter(_.isIndented && isWithinParenRegion)
-      }
+            .getOrElse(false)
 
       if (isTrailingComma) {
         nextToken(currPos, currPos + 1, sepRegions)
       } else if (curr.isNot[Trivia]) {
         if (curr.is[LeftParen]) (RegionParen(false) :: sepRegions, currRef)
         else if (curr.is[LeftBracket]) (RegionBracket :: sepRegions, currRef)
-        else if (curr.is[Comma]) {
-          indentationWithinParenRegion.fold {
-            (sepRegions, currRef)
-          } { region =>
-            (sepRegions.tail, mkOutdent(region))
-          }
+        else if (curr.is[Comma] && indentationWithinParenRegion) {
+          (sepRegions.tail, mkOutdent(prevPos))
         } else if (curr.is[LeftBrace]) {
           val indentInBrace = if (isAheadNewLine(currPos)) countIndent(nextPos) else -1
           // After encountering keyword Enum we add artificial '{' on top of stack.
@@ -513,7 +463,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
               case (_: RegionBrace | _: RegionEnum) :: xs =>
                 (xs, currRef)
               case x :: xs if x.isIndented =>
-                (xs, mkOutdent(x))
+                (xs, mkOutdent(prevPos))
               case _ :: xs =>
                 nextRegions(xs)
               case Nil =>
@@ -528,12 +478,15 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           (nextRegions, currRef)
         } else if (curr.is[EOF]) {
           sepRegions match {
-            case x :: xs if x.isIndented => (xs, mkOutdent(x))
+            case x :: xs if x.isIndented =>
+              // do no include trailing comment/whitespace tokens
+              val pointPos = prevPos + 1
+              (xs, mkOutdent(pointPos))
             case other => (other, currRef)
           }
         } else if (curr.is[RightParen]) {
           sepRegions match {
-            case x :: xs if x.isIndented => (xs, mkOutdent(x))
+            case x :: xs if x.isIndented => (xs, mkOutdent(prevPos))
             case RegionParen(_) :: xs => (xs, currRef)
             case _ => (sepRegions, currRef)
           }
@@ -603,11 +556,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           }
         }
 
-        def getIfCanProduceLF =
-          if (canProduceLF) Some((sepRegions, lastWhitespaceToken))
-          else None
-
-        val resOpt = if (dialect.allowSignificantIndentation) {
+        if (dialect.allowSignificantIndentation) {
           val hasLF = lastNewlinePos != -1 || hasMultilineComment
           if (hasLF && next != null && !isLeadingInfixOperator(next)) {
 
@@ -626,16 +575,16 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
              *    foo()
              *    ```
              */
-            def getOutdentIfNeeded() = sepRegions.headOption
-              .filter { r =>
-                r.isIndented && {
+            def needOutdent: Boolean = {
+              sepRegions.headOption.exists {
+                case r if r.isIndented =>
                   // need to check prev.prev in case of `end match`
                   if (nextIndent < r.indent)
                     prev.isNot[CanContinueOnNextLine] || prev.prev.is[soft.KwEnd]
                   else r.closeOnNonCase && next.isNot[KwCase] && nextIndent == r.indent
-                }
+                case _ => false
               }
-              .map { region => (sepRegions.tail, mkOutdentTo(region, nextPos)) }
+            }
 
             /**
              * Indent is needed in the following cases:
@@ -652,8 +601,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
              *  Indentation after `:` isn't hadled here.
              *  It's produced manually on the parser level.
              */
-            def getIndentIfNeeded = {
-              val ok = nextIndent >= 0 && {
+            def needIndent: Boolean = {
+              val nextIndent = countIndent(nextPos)
+              if (nextIndent > 0) {
 
                 val (currIndent, indentOnArrow) =
                   sepRegions.headOption.fold((0, true))(r => (r.indent, r.indentOnArrow))
@@ -671,24 +621,25 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
                   // check the previous token to avoid infinity loop
                   (!prev.prev.is[soft.KwEnd] && (prev.is[KwMatch] || prev.is[KwCatch])) &&
                   next.is[KwCase] && token.isNot[Indentation.Indent]
-              }
-              if (ok) Some {
-                val region = RegionIndent(nextIndent, prev.is[KwMatch])
-                (region :: sepRegions, mkIndent(lastNewlinePos))
-              }
-              else None
+              } else false
             }
 
-            getOutdentIfNeeded()
-              .orElse { getIndentIfNeeded }
-              .orElse { getIfCanProduceLF }
-          } else None
+            if (needOutdent)
+              (sepRegions.tail, mkOutdent(lastNewlinePos))
+            else if (needIndent)
+              (RegionIndent(nextIndent, prev.is[KwMatch]) :: sepRegions, mkIndent(lastNewlinePos))
+            else if (canProduceLF) {
+              (sepRegions, lastWhitespaceToken)
+            } else
+              nextToken(prevPos, nextPos, sepRegions)
+          } else nextToken(prevPos, nextPos, sepRegions)
+
         } else {
-          getIfCanProduceLF
-        }
-        resOpt match {
-          case Some(res) => res
-          case _ => nextToken(prevPos, nextPos, sepRegions)
+          if (canProduceLF) {
+            (sepRegions, lastWhitespaceToken)
+          } else {
+            nextToken(prevPos, nextPos, sepRegions)
+          }
         }
       }
     }
@@ -879,13 +830,10 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
 
   // NOTE: `startTokenPos` and `endTokenPos` are BOTH INCLUSIVE.
   // This is at odds with the rest of scala.meta, where ends are non-inclusive.
-  sealed trait StartPos {
+  sealed trait Pos {
     def startTokenPos: Int
-  }
-  sealed trait EndPos {
     def endTokenPos: Int
   }
-  sealed trait Pos extends StartPos with EndPos
   case class IndexPos(index: Int) extends Pos {
     def startTokenPos = index
     def endTokenPos = startTokenPos
@@ -905,12 +853,6 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     def startTokenPos = in.tokenPos
     def endTokenPos = in.prevTokenPos
   }
-  case object StartPosPrev extends StartPos {
-    def startTokenPos = in.prevTokenPos
-  }
-  case object EndPosPreOutdent extends EndPos {
-    def endTokenPos = if (in.token.is[Indentation.Outdent]) in.tokenPos else in.prevTokenPos
-  }
   implicit def intToIndexPos(index: Int): IndexPos = IndexPos(index)
   implicit def tokenToTokenPos(token: Token): TokenPos = TokenPos(token)
   implicit def treeToTreePos(tree: Tree): TreePos = TreePos(tree)
@@ -924,31 +866,30 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   def atPos[T <: Tree](token: Token)(body: => T): T = {
     atPos(token.startTokenPos, token.endTokenPos)(body)
   }
-  def atPos[T <: Tree](start: StartPos, end: EndPos)(body: => T): T = {
+  def atPos[T <: Tree](start: Pos, end: Pos)(body: => T): T = {
     val startTokenPos = start.startTokenPos
     atPosWithBody(startTokenPos, body, end)
   }
 
-  def atPosWithBody[T <: Tree](startTokenPos: Int, body: T, endPos: EndPos): T = {
+  def atPosWithBody[T <: Tree](startTokenPos: Int, body: T, endPos: Pos): T = {
     var endTokenPos = endPos.endTokenPos
     if (endTokenPos < startTokenPos) endTokenPos = startTokenPos - 1
 
     def skipBy(range: Range, f: Token => Boolean): Int =
       range.dropWhile(i => f(scannerTokens(i))).headOption.getOrElse(range.start)
     @inline def skipTrivia(range: Range): Int = skipBy(range, _.is[Trivia])
-    @inline def skipWhitespace(range: Range): Int = skipBy(range, _.is[Whitespace])
 
     val rangeFromStart = startTokenPos to endTokenPos
     val (start, end) =
       if (rangeFromStart.isEmpty) (startTokenPos, endTokenPos)
-      else (skipTrivia(rangeFromStart), skipWhitespace(rangeFromStart.reverse))
+      else (skipTrivia(rangeFromStart), skipTrivia(rangeFromStart.reverse))
 
     val endExcl = if (start == end && scannerTokens(start).is[Trivia]) end else end + 1
     val pos = TokenStreamPosition(start, endExcl)
     body.withOrigin(Origin.Parsed(input, dialect, pos))
   }
 
-  def atPosTry[T <: Tree](start: StartPos, end: EndPos)(body: => Try[T]): Try[T] = {
+  def atPosTry[T <: Tree](start: Pos, end: Pos)(body: => Try[T]): Try[T] = {
     val startTokenPos = start.startTokenPos
     body.map(atPosWithBody(startTokenPos, _, end))
   }
@@ -1107,8 +1048,11 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   def isVarargStarParam(allowRepeated: Boolean) =
     dialect.allowPostfixStarVarargSplices && isStar && token.next.is[RightParen] && allowRepeated
   private implicit class XtensionTokenClass(token: Token) {
-    def isClassOrObject = token.is[KwClass] || token.is[KwObject]
-    def isClassOrObjectOrEnum = isClassOrObject || (token.is[Ident] && dialect.allowEnums)
+    def isCaseClassOrObject =
+      token.is[KwCase] && (token.next.is[KwClass] || token.next.is[KwObject])
+
+    def isCaseClassOrObjectOrEnum =
+      isCaseClassOrObject || (token.is[KwCase] && token.next.is[Ident] && dialect.allowEnums)
   }
 
   @classifier
@@ -1226,7 +1170,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   @classifier
   trait CaseIntro {
     def unapply(token: Token): Boolean = {
-      token.is[KwCase] && !token.next.isClassOrObject
+      token.is[KwCase] && !token.isCaseClassOrObject
     }
   }
 
@@ -1237,7 +1181,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       token.is[TemplateIntro] || token.is[DclIntro] ||
       (token.is[Unquote] && token.next.is[DefIntro]) ||
       (token.is[Ellipsis] && token.next.is[DefIntro]) ||
-      (token.is[KwCase] && token.next.isClassOrObjectOrEnum)
+      (token.is[KwCase] && token.isCaseClassOrObjectOrEnum)
     }
   }
 
@@ -1247,7 +1191,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       token.is[Modifier] || token.is[At] ||
       token.is[KwClass] || token.is[KwObject] || token.is[KwTrait] ||
       (token.is[Unquote] && token.next.is[TemplateIntro]) ||
-      (token.is[KwCase] && token.next.isClassOrObjectOrEnum)
+      (token.is[KwCase] && token.isCaseClassOrObjectOrEnum)
     }
   }
 
@@ -1314,7 +1258,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   trait CaseDefEnd {
     def unapply(token: Token): Boolean = {
       token.is[RightBrace] || token.is[EOF] || token.is[Indentation.Outdent] ||
-      (token.is[KwCase] && !token.next.isClassOrObject) || token.is[RightParen] ||
+      (token.is[KwCase] && !token.isCaseClassOrObject) || token.is[RightParen] ||
       (token.is[Ellipsis] && token.next.is[KwCase])
     }
   }
@@ -2769,9 +2713,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     // 2) We need to carry lhsStart/lhsEnd separately from lhs.pos
     //    because their extent may be bigger than lhs because of parentheses or whatnot.
     case class UnfinishedInfix(
-        lhsStart: StartPos,
+        lhsStart: Pos,
         lhs: Lhs,
-        lhsEnd: EndPos,
+        lhsEnd: Pos,
         op: Term.Name,
         targs: List[Type]
     ) {
@@ -2792,7 +2736,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     // Other methods working on the stack are self-explanatory.
     var stack: List[UnfinishedInfix] = Nil
     def head = stack.head
-    def push(lhsStart: StartPos, lhs: Lhs, lhsEnd: EndPos, op: Term.Name, targs: List[Type]): Unit =
+    def push(lhsStart: Pos, lhs: Lhs, lhsEnd: Pos, op: Term.Name, targs: List[Type]): Unit =
       stack ::= UnfinishedInfix(lhsStart, lhs, lhsEnd, op, targs)
     def pop(): UnfinishedInfix =
       try head
@@ -2801,7 +2745,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     def reduceStack(
         stack: List[UnfinishedInfix],
         curr: Rhs,
-        currEnd: EndPos,
+        currEnd: Pos,
         op: Option[Term.Name]
     ): Lhs = {
       val opPrecedence = op.fold(0)(_.precedence)
@@ -2838,7 +2782,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     // then takes the right-hand side (which can have multiple args), e.g. ` (y, z)`,
     // and creates `x + (y, z)`.
     // We need to carry endPos explicitly because its extent may be bigger than rhs because of parent of whatnot.
-    protected def finishInfixExpr(unf: UnfinishedInfix, rhs: Rhs, rhsEnd: EndPos): FinishedInfix
+    protected def finishInfixExpr(unf: UnfinishedInfix, rhs: Rhs, rhsEnd: Pos): FinishedInfix
   }
 
   // Infix syntax in terms is borderline crazy.
@@ -2859,7 +2803,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     def toRhs(fin: FinishedInfix): Rhs = List(fin)
     def toLhs(rhs: Rhs): Lhs = rhs
 
-    protected def finishInfixExpr(unf: UnfinishedInfix, rhs: Rhs, rhsEnd: EndPos): FinishedInfix = {
+    protected def finishInfixExpr(unf: UnfinishedInfix, rhs: Rhs, rhsEnd: Pos): FinishedInfix = {
       val UnfinishedInfix(lhsStart, lhses, lhsEnd, op, targs) = unf
       // `a + (b, c) * d` leads to creation of a tuple!
       val lhs = atPos(lhsStart, lhsEnd)(makeTupleTerm(lhses))
@@ -2897,7 +2841,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     def toRhs(fin: FinishedInfix): Rhs = fin
     def toLhs(rhs: Rhs): Lhs = rhs
 
-    protected def finishInfixExpr(unf: UnfinishedInfix, rhs: Rhs, rhsEnd: EndPos): FinishedInfix = {
+    protected def finishInfixExpr(unf: UnfinishedInfix, rhs: Rhs, rhsEnd: Pos): FinishedInfix = {
       val UnfinishedInfix(lhsStart, lhs, _, op, _) = unf
       val args = rhs match {
         case Pat.Tuple(args) => args.toList; case Lit.Unit() => Nil; case _ => List(rhs)
@@ -2921,7 +2865,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     // rhsStartK/rhsEndK may be bigger than then extent of rhsK,
     // so we really have to track them separately.
     @tailrec
-    def loop(rhsStartK: StartPos, rhsK: ctx.Rhs, rhsEndK: EndPos): ctx.Rhs = {
+    def loop(rhsStartK: Pos, rhsK: ctx.Rhs, rhsEndK: Pos): ctx.Rhs = {
       if (!token.is[Ident] && !token.is[Unquote] &&
         !(token.is[KwMatch] && dialect.allowMatchAsOperator)) {
         // Infix chain has ended.
@@ -2958,9 +2902,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           val lhsStartK = Math.min(rhsStartK.startTokenPos, lhsK.head.startTokenPos)
           ctx.push(lhsStartK, lhsK, rhsEndK, op, targs) // afterwards, ctx.stack = List([a +])
           val preRhsKplus1 = in.token
-          var rhsStartKplus1: StartPos = IndexPos(in.tokenPos)
+          var rhsStartKplus1: Pos = IndexPos(in.tokenPos)
           val rhsKplus1 = argumentExprsOrPrefixExpr()
-          var rhsEndKplus1: EndPos = IndexPos(in.prevTokenPos)
+          var rhsEndKplus1: Pos = IndexPos(in.prevTokenPos)
           if (preRhsKplus1.isNot[LeftBrace] && preRhsKplus1.isNot[LeftParen]) {
             rhsStartKplus1 = TreePos(rhsKplus1.head)
             rhsEndKplus1 = TreePos(rhsKplus1.head)
@@ -3084,26 +3028,24 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
 
   def macroSplice(): Term = autoPos {
     accept[MacroSplice]
-    QuoteSpliceContext.within {
-      if (QuotedPatternContext.isInside())
-        Term.SplicedMacroPat(autoPos(inBraces(pattern())))
-      else
-        Term.SplicedMacroExpr(autoPos(Term.Block(inBraces(blockStatSeq()))))
-
-    }
+    QuoteSpliceContext.enter()
+    val splice = Term.SplicedMacroExpr(autoPos(Term.Block(inBraces(blockStatSeq()))))
+    QuoteSpliceContext.exit()
+    splice
   }
 
   def macroQuote(): Term = autoPos {
     accept[MacroQuote]
-    QuoteSpliceContext.within {
-      if (token.is[LeftBrace]) {
-        Term.QuotedMacroExpr(autoPos(Term.Block(inBraces(blockStatSeq()))))
-      } else if (token.is[LeftBracket]) {
-        Term.QuotedMacroType(inBrackets(typ()))
-      } else {
-        syntaxError("Quotation only works for expressions and types", at = token)
-      }
+    QuoteSpliceContext.enter()
+    val quote = if (token.is[LeftBrace]) {
+      Term.QuotedMacroExpr(autoPos(Term.Block(inBraces(blockStatSeq()))))
+    } else if (token.is[LeftBracket]) {
+      Term.QuotedMacroType(inBrackets(typ()))
+    } else {
+      syntaxError("Quotation only works for expressions and types", at = token)
     }
+    QuoteSpliceContext.exit()
+    quote
   }
 
   def macroQuotedIdent(): Term = autoPos {
@@ -3259,21 +3201,26 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     }
   }
 
-  def caseClause(forceSingleExpr: Boolean = false): Case = atPos(StartPosPrev, EndPosPreOutdent) {
+  def caseClause(forceSingleExpr: Boolean = false): Case = atPos(in.prevTokenPos, auto) {
     if (token.isNot[KwCase]) {
-      def caseBody() = {
-        accept[RightArrow]
-        val start = auto.startTokenPos
-        def parseStatSeq() = blockStatSeq() match {
-          case List(q: Quasi) => q.become[Term.Quasi]
-          case List(term: Term) => term
-          case other => atPos(start, auto)(Term.Block(other))
+      Case(
+        pattern(),
+        guard(), {
+          accept[RightArrow]
+          val start = in.tokenPos
+          def end = in.prevTokenPos
+          def parseStatSeq() = {
+            blockStatSeq() match {
+              case List(q: Quasi) => q.become[Term.Quasi]
+              case List(term: Term) => term
+              case other => atPos(start, end)(Term.Block(other))
+            }
+          }
+          if (token.is[Indentation.Indent]) indented(parseStatSeq())
+          else if (forceSingleExpr) expr(location = BlockStat, allowRepeated = false)
+          else parseStatSeq()
         }
-        if (token.is[Indentation.Indent]) indented(parseStatSeq())
-        else if (forceSingleExpr) expr(location = BlockStat, allowRepeated = false)
-        else parseStatSeq()
-      }
-      Case(pattern(), guard(), caseBody())
+      )
     } else {
       syntaxError("Unexpected `case`", at = token.pos)
     }
@@ -3620,9 +3567,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           val patterns = inParens(if (token.is[RightParen]) Nil else noSeq.patterns())
           makeTuple[Pat](patterns, () => Lit.Unit(), Pat.Tuple(_))
         case MacroQuote() =>
-          QuotedPatternContext.within {
-            Pat.Macro(macroQuote())
-          }
+          Pat.Macro(macroQuote())
         case MacroQuotedIdent() =>
           Pat.Macro(macroQuotedIdent())
         case KwGiven() =>
@@ -4608,6 +4553,8 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         val casePos = in.tokenPos
         next()
         objectDef(mods :+ atPos(casePos, casePos)(Mod.Case()))
+      case Token.At() =>
+        syntaxError("Annotations must precede keyword modifiers", at = token)
       case DefIntro() if dialect.allowToplevelStatements =>
         defOrDclOrSecondaryCtor(mods)
       case _ =>
@@ -5085,8 +5032,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   private val consumeStat: PartialFunction[Token, Stat] = {
     case KwImport() => importStmt()
     case KwExport() => exportStmt()
-    case KwPackage() =>
-      packageOrPackageObjectDef(if (dialect.allowToplevelTerms) consumeStat else topStat)
+    case KwPackage() if !dialect.allowToplevelTerms => packageOrPackageObjectDef()
     case DefIntro() => nonLocalDefOrDcl(secondaryConstructorAllowed = true)
     case EndMarkerIntro() => endMarker()
     case ExprIntro() => stat(expr(location = NoStat, allowRepeated = true))
@@ -5155,13 +5101,14 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     stats.toList
   }
 
+  def topStatSeq(): List[Stat] = statSeq(topStat, errorMsg = "expected class or object definition")
   def topStat: PartialFunction[Token, Stat] = {
     case Ellipsis(_) =>
       ellipsis(1, astInfo[Stat])
     case Unquote() =>
       unquote[Stat]
     case KwPackage() =>
-      packageOrPackageObjectDef(topStat)
+      packageOrPackageObjectDef()
     case KwImport() =>
       importStmt()
     case KwExport() =>
@@ -5308,21 +5255,21 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     stats.toList
   }
 
-  def packageOrPackageObjectDef(statpf: PartialFunction[Token, Stat]): Stat = autoPos {
+  def packageOrPackageObjectDef(): Stat = autoPos {
     require(token.is[KwPackage] && debug(token))
     if (ahead(token.is[KwObject])) packageObjectDef()
-    else packageDef(statpf)
+    else packageDef()
   }
 
-  def packageDef(statpf: PartialFunction[Token, Stat]): Pkg = autoPos {
+  def packageDef(): Pkg = autoPos {
     accept[KwPackage]
     def packageBody = {
       if (isColonEol(token)) {
         accept[Colon]
         in.observeIndented()
-        indented(statSeq(statpf))
+        indented(topStatSeq())
       } else {
-        inBracesOrNil(statSeq(statpf))
+        inBracesOrNil(topStatSeq())
       }
     }
     Pkg(qualId(), packageBody)
@@ -5335,14 +5282,25 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   }
 
   def source(): Source = autoPos {
-    batchSource(if (dialect.allowToplevelTerms) consumeStat else topStat)
+    if (dialect.allowToplevelTerms) scriptSource()
+    else batchSource()
   }
 
   def quasiquoteSource(): Source = entrypointSource()
 
   def entrypointSource(): Source = source()
 
-  def batchSource(statpf: PartialFunction[Token, Stat] = topStat): Source = autoPos {
+  def scriptSource(): Source = autoPos {
+    // WONTFIX: https://github.com/scalameta/scalameta/issues/368
+    if (dialect.toplevelSeparator == "") {
+      Source(parser.statSeq(consumeStat))
+    } else {
+      require(dialect.toplevelSeparator == EOL)
+      Source(parser.statSeq(consumeStat))
+    }
+  }
+
+  def batchSource(): Source = autoPos {
     def inBracelessPackage(): Boolean = token.is[KwPackage] && !ahead(token.is[KwObject]) && ahead {
       qualId()
       if (token.is[LF]) { next() }
@@ -5358,24 +5316,24 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         val startPos = in.tokenPos
         accept[KwPackage]
         val qid = qualId()
-        def inPackage(stats: => List[Stat]) = {
-          val pkg = atPos(startPos, auto)(Pkg(qid, stats))
+        def inPackage(topStats: => List[Stat]) = {
+          val pkg = atPos(startPos, auto)(Pkg(qid, topStats))
           acceptStatSepOpt()
           pkg +: bracelessPackageStats()
         }
         if (token.is[LeftBrace]) {
-          inPackage(inBraces(statSeq(statpf)))
+          inPackage(inBraces(topStatSeq()))
         } else if (isColonEol(token)) {
           next()
           in.observeIndented()
-          inPackage(indented(statSeq(statpf)))
+          inPackage(indented(topStatSeq()))
         } else {
           List(atPos(startPos, auto)(Pkg(qid, bracelessPackageStats())))
         }
       } else if (token.is[LeftBrace]) {
-        inBraces(statSeq(statpf))
+        inBraces(topStatSeq())
       } else {
-        statSeq(statpf)
+        topStatSeq()
       }
     }
     if (inBracelessPackage()) {
@@ -5383,7 +5341,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       accept[KwPackage]
       Source(List(atPos(startPos, auto)(Pkg(qualId(), bracelessPackageStats()))))
     } else {
-      Source(statSeq(statpf))
+      Source(topStatSeq())
     }
   }
 }
diff --git a/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/LegacyScanner.scala b/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/LegacyScanner.scala
index 04dbbf0dd..8c2d6d282 100644
--- a/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/LegacyScanner.scala
+++ b/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/LegacyScanner.scala
@@ -690,34 +690,6 @@ class LegacyScanner(input: Input, dialect: Dialect) {
       next.lastOffset = charOffset - 1
       next.offset = charOffset - 1
     }
-    def identifier() = {
-      do {
-        putChar(ch)
-        nextRawChar()
-      } while (ch != SU && Character.isUnicodeIdentifierPart(ch))
-      next.token = IDENTIFIER
-      next.name = cbuf.toString
-      cbuf.clear()
-      if (kw2legacytoken contains next.name) {
-        next.token = kw2legacytoken(next.name)
-
-        if (next.token == ENUM && !dialect.allowEnums)
-          next.token = IDENTIFIER
-        if (next.token == GIVEN && !dialect.allowGivenUsing)
-          next.token = IDENTIFIER
-        if (next.token == EXPORT && !dialect.allowExportClause)
-          next.token = IDENTIFIER
-        if (next.token == THEN && !dialect.allowSignificantIndentation)
-          next.token = IDENTIFIER
-        if (next.token == TYPELAMBDAARROW && !dialect.allowTypeLambdas)
-          next.token = IDENTIFIER
-        if (next.token == CTXARROW && !dialect.allowGivenUsing)
-          next.token = IDENTIFIER
-
-        if (next.token != IDENTIFIER && next.token != THIS)
-          syntaxError("invalid unquote: `$'ident, `$'BlockExpr, `$'this or `$'_ expected", at = offset)
-      }
-    }
     if (ch == '"') {
       if (multiLine) {
         nextRawChar()
@@ -757,14 +729,36 @@ class LegacyScanner(input: Input, dialect: Dialect) {
           finishStringPart()
           endOffset = charOffset - 3
           nextRawChar()
-          if (Character.isUnicodeIdentifierStart(ch)){
-            putChar('_')
-            identifier()
-          } else next.token = USCORE
+          next.token = USCORE
         } else if (Character.isUnicodeIdentifierStart(ch)) {
           finishStringPart()
           endOffset = charOffset - 3
-          identifier()
+          do {
+            putChar(ch)
+            nextRawChar()
+          } while (ch != SU && Character.isUnicodeIdentifierPart(ch))
+          next.token = IDENTIFIER
+          next.name = cbuf.toString
+          cbuf.clear()
+          if (kw2legacytoken contains next.name) {
+            next.token = kw2legacytoken(next.name)
+
+            if (next.token == ENUM && !dialect.allowEnums)
+              next.token = IDENTIFIER
+            if (next.token == GIVEN && !dialect.allowGivenUsing)
+              next.token = IDENTIFIER
+            if (next.token == EXPORT && !dialect.allowExportClause)
+              next.token = IDENTIFIER
+            if (next.token == THEN && !dialect.allowSignificantIndentation)
+              next.token = IDENTIFIER
+            if (next.token == TYPELAMBDAARROW && !dialect.allowTypeLambdas)
+              next.token = IDENTIFIER
+            if (next.token == CTXARROW && !dialect.allowGivenUsing)
+              next.token = IDENTIFIER
+
+            if (next.token != IDENTIFIER && next.token != THIS)
+              syntaxError("invalid unquote: `$'ident, `$'BlockExpr, `$'this or `$'_ expected", at = offset)
+          }
         } else {
           var supportedCombos = List("`$$'", "`$'ident", "`$'this", "`$'BlockExpr")
           if (dialect.allowSpliceUnderscores) supportedCombos = supportedCombos :+ "`$'_"
diff --git a/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/ScalametaTokenizer.scala b/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/ScalametaTokenizer.scala
index adb5846d1..d4767a457 100644
--- a/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/ScalametaTokenizer.scala
+++ b/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/ScalametaTokenizer.scala
@@ -235,21 +235,21 @@ class ScalametaTokenizer(input: Input, dialect: Dialect) {
               legacyIndex = loop(legacyIndex, returnWhenBraceBalanceHitsZero = true)
               emitSpliceEnd(curr.offset)
               emitContents()
+            } else if (input.chars(dollarOffset + 1) == '_') {
+              emitSpliceStart(dollarOffset)
+              nextToken()
+              emitExpectedToken(USCORE)
+              nextToken()
+              emitSpliceEnd(curr.offset)
+              emitContents()
             } else {
               emitSpliceStart(dollarOffset)
               nextToken()
-              if (input.chars(dollarOffset + 1) == '_' && curr.token == USCORE) {
-                emitExpectedToken(USCORE)
-                nextToken()
-                emitSpliceEnd(curr.offset)
-                emitContents()
-              } else {
-                require(curr.token == IDENTIFIER || curr.token == THIS)
-                emitToken()
-                nextToken()
-                emitSpliceEnd(curr.offset)
-                emitContents()
-              }
+              require(curr.token == IDENTIFIER || curr.token == THIS)
+              emitToken()
+              nextToken()
+              emitSpliceEnd(curr.offset)
+              emitContents()
             }
           } else {
             curr.endOffset -= numQuotes
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala b/scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala
index 2721c6ba4..72751d98e 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala
@@ -117,7 +117,6 @@ object Term {
   @ast class QuotedMacroExpr(body: Term) extends Term
   @ast class QuotedMacroType(tpe: Type) extends Term
   @ast class SplicedMacroExpr(body: Term) extends Term
-  @ast class SplicedMacroPat(pat: Pat) extends Term
   @ast class Match(expr: Term, cases: List[Case] @nonEmpty) extends Term {
     @binaryCompatField(since = "4.4.5")
     private var _mods: List[Mod] = Nil
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 3b96662c8..494cd77fd 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
@@ -399,8 +399,7 @@ object TreeSyntax {
                   case _ => false
                 }
               case _: Lit | _: Term.Ref | _: Term.Function | _: Term.If | _: Term.Match |
-                  _: Term.ApplyInfix | _: Term.QuotedMacroExpr | _: Term.SplicedMacroExpr |
-                  _: Term.SplicedMacroPat =>
+                  _: Term.ApplyInfix | _: Term.QuotedMacroExpr | _: Term.SplicedMacroExpr =>
                 false
               case _ =>
                 true
@@ -605,8 +604,6 @@ object TreeSyntax {
           case head :: Nil => s("${ ", head, " }")
           case other => s("${", r(other.map(i(_)), ""), n("}"))
         }
-      case Term.SplicedMacroPat(pat) =>
-        s("${ ", pat, " }")
       case t: Term.SplicedMacroExpr =>
         m(SimpleExpr, s("$", p(Expr, t.body)))
       case t: Pat.Macro => m(SimplePattern, s(t.body))


Test patch:
diff --git a/bin/test-release.sh b/bin/test-release.sh
index d270452c9..fa538a684 100755
--- a/bin/test-release.sh
+++ b/bin/test-release.sh
@@ -9,7 +9,7 @@ coursier resolve \
   org.scalameta:scalameta_2.11:$version \
   org.scalameta:scalameta_sjs1_2.13:$version \
   org.scalameta:scalameta_sjs1_2.12:$version \
-  org.scalameta:metac_2.13.8:$version \
+  org.scalameta:metac_2.13.6:$version \
   org.scalameta:metac_2.12.15:$version \
   org.scalameta:metac_2.11.12:$version \
   org.scalameta:semanticdb-scalac-core_2.13.8:$version \
diff --git a/community-test/src/test/scala/CommunityDottySuite.scala b/community-test/src/test/scala/CommunityDottySuite.scala
index d1bf82528..0bef3d1e1 100644
--- a/community-test/src/test/scala/CommunityDottySuite.scala
+++ b/community-test/src/test/scala/CommunityDottySuite.scala
@@ -61,7 +61,7 @@ class CommunityDottySuite extends FunSuite {
     CommunityBuild(
       "https://github.com/lampepfl/dotty.git",
       //commit hash from 12.07.2021
-      "c99f6caa74e74a67dd42e8df6ede53c29cd7fce9",
+      "54b89b1cc07b712abb280d64d24b4c363c8bb488",
       "dotty",
       dottyExclusionList
     ),
@@ -164,10 +164,8 @@ class CommunityDottySuite extends FunSuite {
   final val ignoreParts = List(
     "/tests/",
     "/test-resources/scripting/",
-    "/test-resources/repl/",
     "/sbt-test/",
     "/out/",
-    "/language-server/src/dotty/",
-    "/library/src/scala/CanThrow.scala"
+    "/language-server/src/dotty/"
   )
 }
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 2c743ed1e..a09de3b49 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
@@ -358,7 +358,6 @@ class SurfaceSuite extends FunSuite {
       |scala.meta.Term.Return
       |scala.meta.Term.Select
       |scala.meta.Term.SplicedMacroExpr
-      |scala.meta.Term.SplicedMacroPat
       |scala.meta.Term.Super
       |scala.meta.Term.This
       |scala.meta.Term.Throw
diff --git a/tests/jvm/src/test/scala-2.13/scala/meta/tests/trees/ReflectionSuite.scala b/tests/jvm/src/test/scala-2.13/scala/meta/tests/trees/ReflectionSuite.scala
index 3604466b2..7138b1443 100644
--- a/tests/jvm/src/test/scala-2.13/scala/meta/tests/trees/ReflectionSuite.scala
+++ b/tests/jvm/src/test/scala-2.13/scala/meta/tests/trees/ReflectionSuite.scala
@@ -21,7 +21,7 @@ class ReflectionSuite extends FunSuite {
   test("root") {
     assert(symbolOf[scala.meta.Tree].isRoot)
     assertEquals(symbolOf[scala.meta.Tree].asRoot.allBranches.length, 23)
-    assertEquals(symbolOf[scala.meta.Tree].asRoot.allLeafs.length, 347)
+    assertEquals(symbolOf[scala.meta.Tree].asRoot.allLeafs.length, 345)
   }
 
   test("If") {
diff --git a/tests/shared/src/test/scala-2.13/scala/meta/tests/parsers/dotty/Scala3PositionSuite.scala b/tests/shared/src/test/scala-2.13/scala/meta/tests/parsers/dotty/Scala3PositionSuite.scala
index bd82ce18d..154dcf280 100644
--- a/tests/shared/src/test/scala-2.13/scala/meta/tests/parsers/dotty/Scala3PositionSuite.scala
+++ b/tests/shared/src/test/scala-2.13/scala/meta/tests/parsers/dotty/Scala3PositionSuite.scala
@@ -507,28 +507,6 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |""".stripMargin
   )
 
-  checkPositions[Stat](
-    """|foo(
-       |  s =>
-       |    fx
-       |    gx(s)
-       |    /* c1 */,
-       |  "yo"
-       |)
-       |""".stripMargin,
-    """|Term.Function s =>
-       |    fx
-       |    gx(s)
-       |    /* c1 */
-       |Term.Param s
-       |Term.Block fx
-       |    gx(s)
-       |    /* c1 */
-       |Term.Apply gx(s)
-       |Lit.String "yo"
-       |""".stripMargin
-  )
-
   checkPositions[Stat](
     """|inline def encodeFlat =
        |    putInt
@@ -614,7 +592,6 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |      // c1
        |    else
        |      gx
-       |      // c2
        |Self   @@val a = 
        |Defn.Val val a = 
        |    if cond then
@@ -622,54 +599,11 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |      // c1
        |    else
        |      gx
-       |      // c2
        |Term.If if cond then
        |      fx
        |      // c1
        |    else
        |      gx
-       |      // c2
-       |Term.Name fx
-       |      // c1
-       |Term.Name gx
-       |      // c2
-       |""".stripMargin
-  )
-
-  checkPositions[Stat](
-    """|object x:
-       |  val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |""".stripMargin,
-    """|Template :
-       |  val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |Self   @@val a =
-       |Defn.Val val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |Term.If if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |Term.Name fx
-       |      // c1
        |""".stripMargin
   )
 
@@ -691,7 +625,6 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |      // c1
        |    else
        |      gx
-       |      // c2
        |Self   @@val a = 
        |Defn.Val val a = 
        |    if cond then
@@ -699,129 +632,11 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |      // c1
        |    else
        |      gx
-       |      // c2
        |Term.If if cond then
        |      fx
        |      // c1
        |    else
        |      gx
-       |      // c2
-       |Term.Name fx
-       |      // c1
-       |Term.Name gx
-       |      // c2
-       |""".stripMargin
-  )
-
-  // do not add LF to end of input
-  // this test checks `..$comment$EOF` case
-  checkPositions[Stat](
-    """|object y:
-       |  val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2""".stripMargin,
-    """|Template :
-       |  val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |Self   @@val a =
-       |Defn.Val val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |Term.If if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |Term.Name fx
-       |      // c1
-       |""".stripMargin
-  )
-
-  checkPositions[Stat](
-    """|object y {
-       |  val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |}""".stripMargin,
-    """|Template {
-       |  val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |}
-       |Self   @@val a =
-       |Defn.Val val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |Term.If if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    // c2
-       |Term.Name fx
-       |      // c1
-       |""".stripMargin
-  )
-
-  checkPositions[Stat](
-    """|object y {
-       |  val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    /* c2 */}""".stripMargin,
-    """|Template {
-       |  val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    /* c2 */}
-       |Self   @@val a =
-       |Defn.Val val a =
-       |    if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    /* c2 */
-       |Term.If if cond then
-       |      fx
-       |      // c1
-       |    else
-       |      gx
-       |    /* c2 */
-       |Term.Name fx
-       |      // c1
        |""".stripMargin
   )
 
@@ -846,111 +661,4 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |""".stripMargin
   )
 
-  checkPositions[Stat](
-    """|val (x, y) = (foo, bar) match
-       |  case (false, false) => (baz, qux) // c1""".stripMargin,
-    """|Pat.Tuple (x, y)
-       |Term.Match (foo, bar) match
-       |  case (false, false) => (baz, qux) // c1
-       |Term.Tuple (foo, bar)
-       |Case case (false, false) => (baz, qux) // c1
-       |Pat.Tuple (false, false)
-       |Term.Tuple (baz, qux)
-       |""".stripMargin
-  )
-
-  checkPositions[Stat](
-    """|val (x, y) = (foo, bar) match
-       |  case (false, false) => (baz, qux) // c1
-       |""".stripMargin,
-    """|Pat.Tuple (x, y)
-       |Term.Match (foo, bar) match
-       |  case (false, false) => (baz, qux) // c1
-       |Term.Tuple (foo, bar)
-       |Case case (false, false) => (baz, qux) // c1
-       |Pat.Tuple (false, false)
-       |Term.Tuple (baz, qux)
-       |""".stripMargin
-  )
-
-  checkPositions[Stat](
-    """|val (x, y) = (foo, bar) match
-       |  case (true, false) => (baz, qux) // c1
-       |  case (false, true) => (baz, qux) // c2
-       |""".stripMargin,
-    """|Pat.Tuple (x, y)
-       |Term.Match (foo, bar) match
-       |  case (true, false) => (baz, qux) // c1
-       |  case (false, true) => (baz, qux) // c2
-       |Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
-       |Pat.Tuple (true, false)
-       |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
-       |Pat.Tuple (false, true)
-       |Term.Tuple (baz, qux)
-       |""".stripMargin
-  )
-
-  checkPositions[Stat](
-    """|val (x, y) = (foo, bar) match
-       |  case (true, false) => (baz, qux) // c1
-       |  case (false, true) => (baz, qux) // c2""".stripMargin,
-    """|Pat.Tuple (x, y)
-       |Term.Match (foo, bar) match
-       |  case (true, false) => (baz, qux) // c1
-       |  case (false, true) => (baz, qux) // c2
-       |Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
-       |Pat.Tuple (true, false)
-       |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
-       |Pat.Tuple (false, true)
-       |Term.Tuple (baz, qux)
-       |""".stripMargin
-  )
-
-  checkPositions[Stat](
-    """|(foo, bar) match
-       |  case (true, false) => (baz, qux) // c1
-       |  case (false, true) => (baz, qux) // c2""".stripMargin,
-    """|Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
-       |Pat.Tuple (true, false)
-       |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
-       |Pat.Tuple (false, true)
-       |Term.Tuple (baz, qux)
-       |""".stripMargin
-  )
-
-  checkPositions[Stat](
-    """|(foo, bar) match
-       |case (true, false) => (baz, qux) // c1
-       |case (false, true) => (baz, qux) // c2""".stripMargin,
-    """|Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
-       |Pat.Tuple (true, false)
-       |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
-       |Pat.Tuple (false, true)
-       |Term.Tuple (baz, qux)
-       |""".stripMargin
-  )
-
-  checkPositions[Stat](
-    """|(foo, bar) match
-       |case (true, false) => (baz, qux) // c1
-       |case (false, true) => (baz, qux) // c2
-       |""".stripMargin,
-    """|Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
-       |Pat.Tuple (true, false)
-       |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
-       |Pat.Tuple (false, true)
-       |Term.Tuple (baz, qux)
-       |""".stripMargin
-  )
-
 }
diff --git a/tests/shared/src/test/scala-2.13/scala/meta/tests/tokenizers/TokensPositionSuite.scala b/tests/shared/src/test/scala-2.13/scala/meta/tests/tokenizers/TokensPositionSuite.scala
index abf599bba..5e7c16c52 100644
--- a/tests/shared/src/test/scala-2.13/scala/meta/tests/tokenizers/TokensPositionSuite.scala
+++ b/tests/shared/src/test/scala-2.13/scala/meta/tests/tokenizers/TokensPositionSuite.scala
@@ -679,27 +679,6 @@ class TokensPositionSuite extends BasePositionSuite(dialects.Scala213) {
        |""".stripMargin
   )
 
-  checkPositions[Stat](
-    """|trait SampleTrait extends A {
-       |  self: X with B with C => // c1
-       |
-       |  def foo: Boolean = true // c2
-       |  // c3
-       |} // c4""".stripMargin,
-    """|Ctor.Primary trait SampleTrait @@extends A {
-       |Template extends A {
-       |  self: X with B with C => // c1
-       |
-       |  def foo: Boolean = true // c2
-       |  // c3
-       |}
-       |Self self: X with B with C
-       |Type.With X with B with C
-       |Type.With X with B
-       |Defn.Def def foo: Boolean = true
-       |""".stripMargin
-  )
-
   checkPositions[Stat](
     """|object A {
        |  private [this] def foo: Int = ???
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/ModSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/ModSuite.scala
index a67b6c075..d15d39c0d 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/ModSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/ModSuite.scala
@@ -479,4 +479,8 @@ class ModSuite extends ParseSuite {
     ) =
       templStat("protected[foo] private def foo = ???")
   }
+
+  test("Annotation after modifier") {
+    interceptParseErrors("implicit @foo def foo(a: Int): Int")
+  }
 }
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 0f942d5f6..fdd0268cc 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
@@ -26,8 +26,7 @@ class ParseSuite extends FunSuite with CommonTrees {
   def term(code: String)(implicit dialect: Dialect) = code.parseRule(_.expr())
   def pat(code: String)(implicit dialect: Dialect) = code.parseRule(_.pattern())
   def tpe(code: String)(implicit dialect: Dialect) = code.parseRule(_.typ())
-  def topStat(code: String)(implicit dialect: Dialect) =
-    code.parseRule(p => p.statSeq(p.topStat).head)
+  def topStat(code: String)(implicit dialect: Dialect) = code.parseRule(_.topStatSeq().head)
   def templStat(code: String)(implicit dialect: Dialect) = code.parseRule(_.templateStats().head)
   def blockStat(code: String)(implicit dialect: Dialect) = code.parseRule(_.blockStatSeq().head)
   def caseClause(code: String)(implicit dialect: Dialect) = code.parseRule(_.caseClause())
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/ToplevelTermSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/ToplevelTermSuite.scala
deleted file mode 100644
index 7911cf940..000000000
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/ToplevelTermSuite.scala
+++ /dev/null
@@ -1,92 +0,0 @@
-package scala.meta.tests.parsers
-
-import scala.compat.Platform.EOL
-import scala.meta._
-
-import munit._
-
-class ToplevelTermSuite extends FunSuite {
-  implicit val dialect: Dialect = dialects.Scala3.withAllowToplevelTerms(true)
-  test("allowToplevelTerms simple") {
-
-    val sourceString = """def foo(x: Int): Int = x
-                          |foo(x)
-                          |""".trim.stripMargin
-
-    val tree = sourceString.parse[Source].get
-
-    assertEquals(tree.syntax, sourceString)
-    val expected =
-      Source(
-        List(
-          Defn.Def(
-            Nil,
-            Term.Name("foo"),
-            Nil,
-            List(List(Term.Param(Nil, Term.Name("x"), Some(Type.Name("Int")), None))),
-            Some(Type.Name("Int")),
-            Term.Name("x")
-          ),
-          Term.Apply(Term.Name("foo"), List(Term.Name("x")))
-        )
-      )
-    assertEquals(tree.structure, expected.structure)
-  }
-
-  test("allowToplevelTerms and allowPackageStatementsWithToplevelTerms with package") {
-
-    val sourceString = """package bar
-                         |def foo(x: Int): Int = x
-                         |foo(x)
-                         |""".trim.stripMargin
-
-    val tree = sourceString.parse[Source].get
-
-    assertEquals(tree.syntax, sourceString)
-    val expected =
-      Source(
-        List(
-          Pkg(
-            Term.Name("bar"),
-            List(
-              Defn.Def(
-                Nil,
-                Term.Name("foo"),
-                Nil,
-                List(List(Term.Param(Nil, Term.Name("x"), Some(Type.Name("Int")), None))),
-                Some(Type.Name("Int")),
-                Term.Name("x")
-              ),
-              Term.Apply(Term.Name("foo"), List(Term.Name("x")))
-            )
-          )
-        )
-      )
-    assertEquals(tree.structure, expected.structure)
-  }
-
-  test("allowToplevelTerms and allowPackageStatementsWithToplevelTerms no package") {
-    val sourceString = """def foo(x: Int): Int = x
-                         |foo(x)
-                         |""".trim.stripMargin
-
-    val tree = sourceString.parse[Source].get
-
-    assertEquals(tree.syntax, sourceString)
-    val expected = Source(
-      List(
-        Defn.Def(
-          Nil,
-          Term.Name("foo"),
-          Nil,
-          List(List(Term.Param(Nil, Term.Name("x"), Some(Type.Name("Int")), None))),
-          Some(Type.Name("Int")),
-          Term.Name("x")
-        ),
-        Term.Apply(Term.Name("foo"), List(Term.Name("x")))
-      )
-    )
-    assertEquals(tree.structure, expected.structure)
-  }
-
-}
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
index 3e45c786d..94deec2b9 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
@@ -110,36 +110,6 @@ class MacroSuite extends BaseDottySuite {
         List(Case(Pat.Macro(Term.QuotedMacroExpr(Term.Block(List(tname("a"))))), None, Lit.Int(1)))
       )
     )
-
-    val layoutMatchWithSplice = s"x match {\n  case '{ $${ bind @ '{ a } } } => 1\n}"
-    runTestAssert[Stat](
-      s"x match { case '{ $${bind@'{a}} } => 1 }",
-      assertLayout = Some(layoutMatchWithSplice)
-    )(
-      Term.Match(
-        tname("x"),
-        List(
-          Case(
-            Pat.Macro(
-              Term.QuotedMacroExpr(
-                Term.Block(
-                  List(
-                    Term.SplicedMacroPat(
-                      Pat.Bind(
-                        Pat.Var(tname("bind")),
-                        Pat.Macro(Term.QuotedMacroExpr(Term.Block(List(tname("a")))))
-                      )
-                    )
-                  )
-                )
-              )
-            ),
-            None,
-            Lit.Int(1)
-          )
-        )
-      )
-    )
   }
 
   test("macro-brackets") {
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala
index 21027ecba..4128db216 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala
@@ -442,6 +442,18 @@ class MinorDottySuite extends BaseDottySuite {
     )
   }
 
+  test("annotation after modifier") {
+    runTestError[Stat](
+      "implicit @foo def foo(): Int",
+      "Annotations must precede keyword modifiers"
+    )
+
+    runTestError[Stat](
+      "{ inline @foo def foo(): Int }",
+      "; expected but @ found"
+    )
+  }
+
   test("unchecked-annotation") {
     runTestAssert[Stat]("val a :: Nil:  @unchecked = args")(
       Defn.Val(
diff --git a/tests/shared/src/test/scala/scala/meta/tests/tokenizers/TokenizerSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/tokenizers/TokenizerSuite.scala
index 7d45eb417..954e9e588 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/tokenizers/TokenizerSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/tokenizers/TokenizerSuite.scala
@@ -759,23 +759,6 @@ class TokenizerSuite extends BaseTokenizerSuite {
     """.trim.stripMargin)
   }
 
-  test("interpolation-underscore") {
-    assertNoDiff(
-      tokenize("""s"checking redundancy in $_match"""").map(_.structure).mkString("\n"),
-      """|BOF [0..0)
-         |s [0..1)
-         |" [1..2)
-         |checking redundancy in  [2..25)
-         |$ [25..26)
-         |_match [26..32)
-         | [32..32)
-         | [32..32)
-         |" [32..33)
-         |EOF [33..33)
-         |""".stripMargin
-    )
-  }
-
   test("$this") {
     assert(tokenize("q\"$this\"").map(_.structure).mkString("\n") == """
       |BOF [0..0)