Repository: scalameta/scalameta
Pull request #3643 (2024-03-12 08:05:09)

ScalametaParser: refactor pattern3
Helps with #3631.


Patch:
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 66a803842..dcdf381ca 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
@@ -530,10 +530,6 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   def isIdentOf(tok: Token, name: String) = isIdentAnd(tok, _ == name)
   @inline def isStar: Boolean = isStar(token)
   def isStar(tok: Token): Boolean = isIdentOf(tok, "*")
-  def isVarargStarParam(allowRepeated: Boolean) =
-    isStar && isVarargParamOnStar(allowRepeated)
-  def isVarargParamOnStar(allowRepeated: Boolean) =
-    allowRepeated && dialect.allowPostfixStarVarargSplices && peekToken.is[RightParen]
 
   private trait MacroIdent {
     protected def ident(token: Token): Option[String]
@@ -599,8 +595,8 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     extraSkip
     // unquote returns a rank=0 quasi tree
     val tree = token match {
-      case LeftParen() => inParensOnOpen(unquote[T])
-      case LeftBrace() => inBracesOnOpen(unquote[T])
+      case _: LeftParen => inParensOnOpen(unquote[T])
+      case _: LeftBrace => inBracesOnOpen(unquote[T])
       case t: Unquote => unquote[T](t)
       case t => syntaxError(s"$$, ( or { expected but ${t.name} found", at = t)
     }
@@ -739,7 +735,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           ellipsis[Type](t)
         case t: Unquote =>
           unquote[Type](t)
-        case KwImplicit() if !hasImplicits =>
+        case _: KwImplicit if !hasImplicits =>
           next()
           hasImplicits = true
           paramOrType(modsBuf)
@@ -834,8 +830,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         else infixTypeOrTuple()
 
       getAfterOptNewLine(token match {
-        case RightArrow() => Some(typeFuncOnArrow(startPos, t :: Nil, Type.Function(_, _)))
-        case ContextArrow() => Some(typeFuncOnArrow(startPos, t :: Nil, Type.ContextFunction(_, _)))
+        case _: RightArrow => Some(typeFuncOnArrow(startPos, t :: Nil, Type.Function(_, _)))
+        case _: ContextArrow =>
+          Some(typeFuncOnArrow(startPos, t :: Nil, Type.ContextFunction(_, _)))
         case _: KwForsome => next(); Some(Type.Existential(t, existentialStats()))
         case _: KwMatch if dialect.allowTypeMatch => next(); Some(Type.Match(t, typeCaseClauses()))
         case _ => None
@@ -1067,10 +1064,10 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       }
       val res = token match {
         case _: Ident if peekToken.is[Dot] => pathSimpleType()
-        case LeftParen() => makeTupleType(startPos, inParensOnOpen(types()))
+        case _: LeftParen => makeTupleType(startPos, inParensOnOpen(types()))
         case MacroSplicedIdent(ident) =>
           Type.Macro(macroSplicedIdent(ident))
-        case MacroSplice() =>
+        case _: MacroSplice =>
           Type.Macro(macroSplice())
         case _: Underscore if inMatchType =>
           next(); Type.PatWildcard()
@@ -1105,10 +1102,10 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
 
     @tailrec
     private final def simpleTypeRest(t: Type, startPos: Int): Type = token match {
-      case Hash() =>
+      case _: Hash =>
         next()
         simpleTypeRest(autoEndPos(startPos)(Type.Project(t, typeName())), startPos)
-      case LeftBracket() =>
+      case _: LeftBracket =>
         simpleTypeRest(autoEndPos(startPos)(Type.Apply(t, typeArgs())), startPos)
       case _ => t
     }
@@ -1199,7 +1196,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         if (allowInfix) {
           val t = if (token.is[LeftParen]) tupleInfixType() else compoundType()
           token match {
-            case KwForsome() => next(); copyPos(t)(Type.Existential(t, existentialStats()))
+            case _: KwForsome => next(); copyPos(t)(Type.Existential(t, existentialStats()))
             case _: Unquote | Keywords.NotPatAlt() => infixTypeRest(t)
             case _ => t
           }
@@ -1375,7 +1372,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         partsBuf += atCurPos(Lit.String(value))
         next()
         loop()
-      case Interpolation.SpliceStart() =>
+      case _: Interpolation.SpliceStart =>
         next()
         argsBuf += arg
         accept[Interpolation.SpliceEnd]
@@ -1404,7 +1401,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         partsBuf += atCurPos(Lit.String(value))
         next()
         loop()
-      case Xml.SpliceStart() =>
+      case _: Xml.SpliceStart =>
         next()
         argsBuf += arg
         accept[Xml.SpliceEnd]
@@ -1598,9 +1595,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         ifClause(List(inlineMod()))
       case _ if isInlineMatchMod(tokenPos) =>
         inlineMatchClause(List(inlineMod()))
-      case KwIf() =>
+      case _: KwIf =>
         ifClause()
-      case KwTry() =>
+      case _: KwTry =>
         next()
         val body: Term = token match {
           case _ if dialect.allowTryWithAnyExpr => expr()
@@ -1633,7 +1630,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         }
         else tryWithCases(Nil)
 
-      case KwWhile() =>
+      case _: KwWhile =>
         next()
         val cond =
           if (token.is[LeftParen])
@@ -1642,16 +1639,16 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
             try expr()
             finally acceptAfterOptNL[KwDo]
         Term.While(cond, expr())
-      case KwDo() if dialect.allowDoWhile =>
+      case _: KwDo if dialect.allowDoWhile =>
         next()
         val body = expr()
         while (StatSep(token)) next()
         accept[KwWhile]
         val cond = condExpr()
         Term.Do(body, cond)
-      case KwDo() =>
+      case _: KwDo =>
         syntaxError("do {...} while (...) syntax is no longer supported", at = token)
-      case KwFor() =>
+      case _: KwFor =>
         next()
         val enums: List[Enumerator] =
           if (acceptOpt[LeftBrace]) inBracesAfterOpen(enumerators())
@@ -1676,14 +1673,14 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         } else {
           Term.For(enums, expr())
         }
-      case KwReturn() =>
+      case _: KwReturn =>
         next()
         if (isExprIntro(token, tokenPos)) Term.Return(expr())
         else Term.Return(autoPos(Lit.Unit()))
-      case KwThrow() =>
+      case _: KwThrow =>
         next()
         Term.Throw(expr())
-      case KwImplicit() =>
+      case _: KwImplicit =>
         next()
         implicitClosure(location)
       case _ =>
@@ -1729,7 +1726,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
               iter(addPos(Term.Ascribe(t, typeOrInfixType(location))))
             }
         }
-      case Ident("*") if isVarargParamOnStar(allowRepeated) =>
+      case soft.StarSplice() if allowRepeated && peekToken.is[RightParen] =>
         repeatedTerm(t, next)
       case _: KwMatch =>
         next()
@@ -2171,7 +2168,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         case t: Unquote =>
           val op = unquote[Term.Name](t)
           Some(getPostfixOrNextRhs(op))
-        case t: Ident if !isVarargStarParam(allowRepeated) =>
+        case t: Ident if !(allowRepeated && soft.StarSplice(t) && peekToken.is[RightParen]) =>
           val op = atCurPosNext(Term.Name(t.value))
           Some(getPostfixOrNextRhs(op))
         case _: KwMatch if dialect.allowMatchAsOperator =>
@@ -2242,9 +2239,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     var canApply = true
     val startPos = tokenPos
     (token match {
-      case MacroQuote() =>
+      case _: MacroQuote =>
         Success(macroQuote())
-      case MacroSplice() =>
+      case _: MacroSplice =>
         Success(macroSplice())
       case MacroQuotedIdent(ident) =>
         Success(macroQuotedIdent(ident))
@@ -2256,16 +2253,16 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         Success(interpolateTerm())
       case _: Xml.Start =>
         Success(xmlTerm())
-      case Ident(_) | KwThis() | KwSuper() | Unquote() =>
+      case _: Ident | _: KwThis | _: KwSuper | _: Unquote =>
         Success(path().become[Term])
-      case Underscore() =>
+      case _: Underscore =>
         Success(atCurPosNext(Term.Placeholder()))
-      case LeftParen() =>
+      case _: LeftParen =>
         Success(inParensOrTupleOrUnitExpr(allowRepeated = allowRepeated))
-      case LeftBrace() =>
+      case _: LeftBrace =>
         canApply = false
         Success(blockExprOnBrace())
-      case KwNew() =>
+      case _: KwNew =>
         canApply = false
         Success(autoPos {
           next()
@@ -2277,9 +2274,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
               Term.NewAnonymous(other)
           }
         })
-      case LeftBracket() if dialect.allowPolymorphicFunctions =>
+      case _: LeftBracket if dialect.allowPolymorphicFunctions =>
         Success(polyFunction())
-      case Indentation.Indent() =>
+      case _: Indentation.Indent =>
         Success(blockExprOnIndent())
       case _ =>
         Failure(new ParseException(token.pos, "illegal start of simple expression"))
@@ -2334,7 +2331,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       }
     }
     token match {
-      case Dot() =>
+      case _: Dot =>
         next()
         if (dialect.allowMatchAsOperator && acceptOpt[KwMatch]) {
           val clause = matchClause(t, startPos)
@@ -2344,7 +2341,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         } else {
           simpleExprRest(selector(t, startPos), canApply = true, startPos = startPos)
         }
-      case LeftBracket() =>
+      case _: LeftBracket =>
         t match {
           case _: Quasi | _: Term.Name | _: Term.Select | _: Term.Apply | _: Term.ApplyInfix |
               _: Term.ApplyUnary | _: Term.New | _: Term.Placeholder | _: Term.ApplyUsing |
@@ -2363,7 +2360,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         simpleExprRest(arguments, canApply = true, startPos = startPos)
       case _: Colon if canApply =>
         getFewerBracesApplyOnColon(t, startPos).getOrElse(t)
-      case Underscore() =>
+      case _: Underscore =>
         next()
         addPos(Term.Eta(t))
       case _ =>
@@ -2845,19 +2842,15 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       val base = ctx.stack
       @tailrec
       def loop(rhs: ctx.Typ): ctx.Typ = {
-        val op = token match {
-          case _: Unquote | Keywords.NotPatAlt() => Some(termName())
-          case _ => None
-        }
-        val lhs1 = ctx.reduceStack(base, rhs, rhs, op)
-        op match {
-          case Some(op) =>
+        @inline def lhs(opOpt: Option[Term.Name]) = ctx.reduceStack(base, rhs, rhs, opOpt)
+        token match {
+          case _: Unquote | Keywords.NotPatAlt() =>
+            val op = termName()
             expectNot[LeftBracket]("infix patterns cannot have type arguments")
-            ctx.push(ctx.UnfinishedInfix(lhs1, op))
-            val rhs1 = simplePattern(badPattern3, isRhs = true)
-            loop(rhs1)
-          case None =>
-            lhs1
+            ctx.push(ctx.UnfinishedInfix(lhs(Some(op)), op))
+            loop(simplePattern(badPattern3, isRhs = true))
+          case _ =>
+            lhs(None)
         }
       }
       loop(lhs)
@@ -2917,7 +2910,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
             sid match {
               case name: Term.Name.Quasi => name.become[Pat]
               case name: Term.Name =>
-                if (dialect.allowPostfixStarVarargSplices && isStar && tryAheadNot[Ident])
+                if (soft.StarSplice(token) && tryAheadNot[Ident])
                   Pat.Repeated(name)
                 else if ((!sidToken.isBackquoted || isForComprehension) && {
                     val first = name.value.head
@@ -3302,7 +3295,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
 
   def paramType(): Type =
     autoPosOpt(token match {
-      case RightArrow() =>
+      case _: RightArrow =>
         val t = autoPos {
           next()
           Type.ByName(typ())
@@ -3572,7 +3565,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         }
       }
       def importeesHaveWildcard = importees.exists {
-        case Importee.Wildcard() => true
+        case _: Importee.Wildcard => true
         case _ => false
       }
       if (dialect.allowGivenImports)
@@ -3644,28 +3637,28 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     )
     onlyAcceptMod[Mod.Opaque, KwType](mods, "opaque not allowed here. Only types can be opaque.")
     token match {
-      case KwVal() | KwVar() =>
+      case _: KwVal | _: KwVar =>
         patDefOrDcl(mods)
-      case KwGiven() =>
+      case _: KwGiven =>
         givenDecl(mods)
-      case KwDef() =>
+      case _: KwDef =>
         if (!secondaryConstructorAllowed && peekToken.is[KwThis])
           syntaxError("Illegal secondary constructor", at = token.pos)
         funDefOrDclOrExtensionOrSecondaryCtor(mods)
-      case KwType() =>
+      case _: KwType =>
         typeDefOrDcl(mods)
       case _ if isKwExtension(tokenPos) =>
         extensionGroupDecl(mods)
-      case KwCase() if dialect.allowEnums && enumCaseAllowed && peekToken.is[Ident] =>
+      case _: KwCase if dialect.allowEnums && enumCaseAllowed && peekToken.is[Ident] =>
         mods.find(mod => !mod.isAccessMod && !mod.is[Mod.Annot]) match {
           case Some(mod) =>
             syntaxError("Only access modifiers allowed on enum case", at = mod.pos)
           case None =>
             enumCaseDef(mods)
         }
-      case KwCase() if dialect.allowEnums && peekToken.is[Ident] =>
+      case _: KwCase if dialect.allowEnums && peekToken.is[Ident] =>
         syntaxError("Enum cases are only allowed in enums", at = token.pos)
-      case KwIf() if mods.size == 1 && mods.head.is[Mod.Inline] =>
+      case _: KwIf if mods.size == 1 && mods.head.is[Mod.Inline] =>
         ifClause(mods)
       case _ if isExprIntro(token, tokenPos) && mods.size == 1 && mods.head.is[Mod.Inline] =>
         inlineMatchClause(mods)
@@ -3955,9 +3948,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       Defn.Type(mods, name, tparams, typeIndentedOpt(), bounds)
     } else {
       token match {
-        case Equals() => next(); aliasType()
-        case Supertype() | Subtype() | Comma() | RightBrace() => abstractType()
-        case StatSep() | Indentation.Outdent() => abstractType()
+        case _: Equals => next(); aliasType()
+        case _: Supertype | _: Subtype | _: Comma | _: RightBrace => abstractType()
+        case StatSep() | _: Indentation.Outdent => abstractType()
         case _ => syntaxError("`=', `>:', or `<:' expected", at = token)
       }
     }
@@ -3974,19 +3967,19 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       rejectMod[Mod.Lazy](mods, Messages.InvalidLazyClasses)
     }
     token match {
-      case KwTrait() =>
+      case _: KwTrait =>
         traitDef(mods)
-      case KwEnum() =>
+      case _: KwEnum =>
         enumDef(mods)
-      case KwClass() =>
+      case _: KwClass =>
         classDef(mods)
-      case KwCase() if tryAhead[KwClass] =>
+      case _: KwCase if tryAhead[KwClass] =>
         classDef(mods :+ atPos(prevTokenPos)(Mod.Case()))
-      case KwObject() =>
+      case _: KwObject =>
         objectDef(mods)
-      case KwCase() if tryAhead[KwObject] =>
+      case _: KwCase if tryAhead[KwObject] =>
         objectDef(mods :+ atPos(prevTokenPos)(Mod.Case()))
-      case Token.At() =>
+      case _: At =>
         syntaxError("Annotations must precede keyword modifiers", at = token)
       case _ if dialect.allowToplevelStatements && isDefIntro(tokenPos) =>
         defOrDclOrSecondaryCtor(mods)
@@ -4203,7 +4196,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
 
   def entrypointInit(): Init = {
     token match {
-      case KwThis() => initInsideConstructor()
+      case _: KwThis => initInsideConstructor()
       case _ => initInsideTemplate()
     }
   }
@@ -4483,9 +4476,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   /* -------- STATSEQS ------------------------------------------- */
 
   private val consumeStat: PartialFunction[Token, Stat] = {
-    case KwImport() => importStmt()
-    case KwExport() => exportStmt()
-    case KwPackage() =>
+    case _: KwImport => importStmt()
+    case _: KwExport => exportStmt()
+    case _: KwPackage =>
       packageOrPackageObjectDef(if (dialect.allowToplevelTerms) consumeStat else topStat)
     case _ if isDefIntro(tokenPos) => nonLocalDefOrDcl(secondaryConstructorAllowed = true)
     case _ if isEndMarkerIntro(tokenPos) => endMarker()
@@ -4559,11 +4552,11 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       ellipsis[Stat](t, 1)
     case t: Unquote =>
       unquote[Stat](t)
-    case KwPackage() =>
+    case _: KwPackage =>
       packageOrPackageObjectDef(topStat)
-    case KwImport() =>
+    case _: KwImport =>
       importStmt()
-    case KwExport() =>
+    case _: KwExport =>
       exportStmt()
     case _ if isTemplateIntro(tokenPos) =>
       topLevelTmplDef
@@ -4595,9 +4588,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       enumCaseAllowed: Boolean = false,
       secondaryConstructorAllowed: Boolean = false
   ): PartialFunction[Token, Stat] = {
-    case KwImport() =>
+    case _: KwImport =>
       importStmt()
-    case KwExport() =>
+    case _: KwExport =>
       exportStmt()
     case _ if isDefIntro(tokenPos) =>
       nonLocalDefOrDcl(enumCaseAllowed, secondaryConstructorAllowed)
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/SoftKeywords.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/SoftKeywords.scala
index b45d385b6..29dfe011b 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/SoftKeywords.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/SoftKeywords.scala
@@ -18,6 +18,7 @@ class SoftKeywords(dialect: Dialect) {
   object KwExtension extends IsWithName(dialect.allowExtensionMethods, "extension")
   object KwErased extends IsWithName(dialect.allowErasedDefs, "erased")
 
+  object StarSplice extends IsWithName(dialect.allowPostfixStarVarargSplices, "*")
   object StarAsTypePlaceholder
       extends AsWithFunc(
         dialect.allowStarAsTypePlaceholder,


Test patch:
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 33626f314..c4e4d31aa 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
@@ -907,14 +907,13 @@ class MinorDottySuite extends BaseDottySuite {
   }
 
   test("vararg-wildcard-like-postfix-star") {
-    runTestAssert[Stat]("val lst = List(0, arr`*`)", "val lst = List(0, arr*)")(
-      Defn.Val(
-        Nil,
-        List(Pat.Var(tname("lst"))),
-        None,
-        Term.Apply(tname("List"), List(int(0), Term.Repeated(tname("arr"))))
-      )
+    val tree = Defn.Val(
+      Nil,
+      List(Pat.Var(tname("lst"))),
+      None,
+      Term.Apply(tname("List"), List(lit(0), Term.Select(tname("arr"), tname("*"))))
     )
+    runTestAssert[Stat]("val lst = List(0, arr`*`)", "val lst = List(0, arr.*)")(tree)
   }
 
   test("non-vararg-infix-star") {
@@ -962,27 +961,11 @@ class MinorDottySuite extends BaseDottySuite {
   }
 
   test("vararg-wildcard-like-postfix-start-pat") {
-    runTestAssert[Stat](
-      "a match {case List(xs`*`) => }",
-      assertLayout = Some(
-        """|a match {
-           |  case List(xs*) =>
-           |}
-           |""".stripMargin
-      )
-    )(
-      Term.Match(
-        tname("a"),
-        List(
-          Case(
-            Pat.Extract(tname("List"), List(Pat.Repeated(tname("xs")))),
-            None,
-            Term.Block(Nil)
-          )
-        ),
-        Nil
-      )
-    )
+    val error =
+      """|<input>:1: error: bad simple pattern: use _* to match a sequence
+         |a match {case List(xs`*`) => }
+         |                        ^""".stripMargin
+    runTestError[Stat]("a match {case List(xs`*`) => }", error)
   }
 
   test("empty-case-class") {