Repository: scalameta/scalameta (scala)

Issue:
Inconsistency with Scalac on package private fields of case classes
Scala [3.2.2](https://scastie.scala-lang.org/yKRRHRKjStu1EsBnJ0NhKA), [2.13.10](https://scastie.scala-lang.org/KqrtO9P6TcGnZeogV6TmCw), and [2.12.17](https://scastie.scala-lang.org/0g6bJlZ9RfSZvwx9iK4QdQ) at least (and probably every other version support package private fields of case classes without the `val` keyword:

```scala
package example

case class Foo(private[example] field: String)
```

Semantic db prior to 4.7.5 also supported it, but starting in 4.7.5, semanticdb throws exceptions like:
```
[error] failed to generate semanticdb for .../src/main/scala/example/Hello.scala:
[error] .../src/main/scala/example/Hello.scala:4: error: val expected but identifier found
[error]   case class Foo(private[example] field: String)
[error]                                   ^
[error]         at scala.meta.internal.parsers.Reporter.syntaxError(Reporter.scala:16)
[error]         at scala.meta.internal.parsers.Reporter.syntaxError$(Reporter.scala:16)
[error]         at scala.meta.internal.parsers.Reporter$$anon$1.syntaxError(Reporter.scala:22)
[error]         at scala.meta.internal.parsers.Reporter.syntaxError(Reporter.scala:17)
[error]         at scala.meta.internal.parsers.Reporter.syntaxError$(Reporter.scala:17)
```

I'm guessing this is actually a bug in Scalac, but ideally the behavior of semanticdb would be consistent with that of Scalac.

Pull request description:
Fixes #3122.

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 c2ac790e6a..270febc87f 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
@@ -3099,7 +3099,12 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         mods += autoEndPos(prevTokenPos)(Mod.Inline())
       }
     }
-    val hasExplicitMods = mods.view.drop(numAnnots).exists(!_.is[Mod.Quasi])
+    val hasExplicitMods = mods.view.drop(numAnnots).exists {
+      case _: Mod.Quasi => false
+      case m: Mod.Private => m.within.is[Name.Anonymous]
+      case m: Mod.Protected => m.within.is[Name.Anonymous]
+      case _ => true
+    }
 
     mod.foreach { mod =>
       val clazz = mod.getClass


Test patch:
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 3b9b532ae3..82b7f70774 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
@@ -838,4 +838,28 @@ class ModSuite extends ParseSuite {
     assert(actual.contains(expected), actual)
   }
 
+  test("#3122 missing val after package-private modifier") {
+    val code = "case class Foo(private[example] field: String)"
+    val expected =
+      Defn.Class(
+        List(Mod.Case()),
+        Type.Name("Foo"),
+        Nil,
+        Ctor.Primary(
+          Nil,
+          Name(""),
+          List(
+            Term.Param(
+              List(Mod.Private(Name("example"))),
+              Term.Name("field"),
+              Some(Type.Name("String")),
+              None
+            ) :: Nil
+          )
+        ),
+        Template(Nil, Nil, Self(Name(""), None), Nil, Nil)
+      )
+    assertTree(templStat(code))(expected)
+  }
+
 }