Repository: scalameta/scalameta
Pull request #3639 (2024-03-12 07:58:03)

Keywords: match allowQuestionMarkAsTypeWildcard
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 f8562e114..5d92f7e58 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
@@ -1078,8 +1078,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           next(); Type.AnonymousParam(None)
         case _: Underscore =>
           wildcardType()
-        case Ident("?") if !inMatchType && dialect.allowQuestionMarkAsTypeWildcard =>
-          wildcardType()
+        case soft.QuestionMarkAsTypeWildcard() if !inMatchType => wildcardType()
         case soft.StarAsTypePlaceholder(value) if !inMatchType =>
           next(); anonymousParamWithVariant(value)
         case Ident(value @ ("+" | "-"))
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 29dfe011b..eb281317c 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
@@ -27,5 +27,6 @@ class SoftKeywords(dialect: Dialect) {
           if (last >= 0 && x.charAt(last) == '*') Some(x.substring(0, last)) else None
         }
       )
+  object QuestionMarkAsTypeWildcard extends IsWithName(dialect.allowQuestionMarkAsTypeWildcard, "?")
 
 }
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 c10e4618b..2b60aeebe 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
@@ -110,6 +110,8 @@ object TreeSyntax {
         seq += "+*" -> Seq(classTag[Type.ArgClause])
       }
 
+      if (dialect.allowQuestionMarkAsTypeWildcard) seq += "?" -> Seq(classTag[Type.ArgClause])
+
       seq.result().groupBy(_._1).map { case (k, vs) =>
         k -> vs.flatMap(_._2).distinct.map(_.runtimeClass)
       }


Test patch:
diff --git a/tests/jvm/src/test/scala/scala/meta/tests/prettyprinters/RegressionSyntaxSuite.scala b/tests/jvm/src/test/scala/scala/meta/tests/prettyprinters/RegressionSyntaxSuite.scala
index 2ba2bcd3f..3bc581a32 100644
--- a/tests/jvm/src/test/scala/scala/meta/tests/prettyprinters/RegressionSyntaxSuite.scala
+++ b/tests/jvm/src/test/scala/scala/meta/tests/prettyprinters/RegressionSyntaxSuite.scala
@@ -18,7 +18,7 @@ class RegressionSyntaxSuite extends ParseSuite {
     assertEquals(reprintedCode, expected)
   }
 
-  check("question-mark", "type T = List[?]", "type T = List[?]")
+  check("question-mark 211->213", "type T = List[?]", "type T = List[`?`]")(dialects.Scala211)
   check("underscore", "type T = List[_]", "type T = List[_]")
 
   check("211->3x", "type T = List[_]", "type T = List[?]", dialects.Scala3Future)(dialects.Scala211)
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 c4e4d31aa..a1fa3e4ea 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
@@ -403,19 +403,14 @@ class MinorDottySuite extends BaseDottySuite {
   }
 
   test("question-type-like") {
-    val treeWithoutBounds = Decl.Val(
-      Nil,
-      List(Pat.Var(tname("stat"))),
-      Type.Apply(pname("Tree"), List(Type.Wildcard(noBounds)))
-    )
-    runTestAssert[Stat]("val stat: Tree[`?`]", "val stat: Tree[?]")(treeWithoutBounds)
-    runTestAssert[Stat]("val stat: Tree[`?` >: Untyped]", "val stat: Tree[? >: Untyped]")(
-      Decl.Val(
-        Nil,
-        List(Pat.Var(tname("stat"))),
-        Type.Apply(pname("Tree"), List(Type.Wildcard(loBound("Untyped"))))
-      )
-    )
+    val treeWithoutBounds =
+      Decl.Val(Nil, List(Pat.Var(tname("stat"))), Type.Apply(pname("Tree"), List(pname("?"))))
+    runTestAssert[Stat]("val stat: Tree[`?`]")(treeWithoutBounds)
+    val errorWithBounds =
+      """|<input>:1: error: ] expected but >: found
+         |val stat: Tree[`?` >: Untyped]
+         |                   ^""".stripMargin
+    runTestError[Stat]("val stat: Tree[`?` >: Untyped]", errorWithBounds)
   }
 
   test("lazy-val-toplevel") {