Repository: scalameta/scalameta (scala)

Issue:
`PositionExceptionImpl` complaining about expected `)` when encountering valid `<-` in for comprehension
## Configuration (required) ##

Please paste the contents of your `.scalafmt.conf` file here:
```
version = 3.8.1
runner.dialect = scala212source3
maxColumn = 120
project.git = true

# http://docs.scala-lang.org/style/scaladoc.html recommends the JavaDoc style.
# scala/scala is written that way too https://github.com/scala/scala/blob/v2.12.2/src/library/scala/Predef.scala
docstrings.style = Asterisk
literals.hexDigits = upper
project.layout = StandardConvention
```

## Command-line parameters (required) ##

When I run scalafmt via CLI like this: `scalafmt`

## Steps

Given code like this:
```scala
// #slickQueryImplicitJoin
val slick2 =
  // #slickQueryImplicitJoin
  // or equivalent for-expression:
  (for (
    p <- people
    a <- addresses if p.addressId === a.id
  ) yield (p.name, a.city)).result
// #slickQueryImplicitJoin
val ((sqlRes, slickRes), slick2Res) = Await.result(db.run(sql zip slick zip slick2), Duration.Inf)
assert(sqlRes == slickRes)
assert(slickRes == slick2Res)
assert(sqlRes.size > 0)
```

## Problem

Scalafmt fails with

```
org.scalafmt.dynamic.exceptions.PositionExceptionImpl: /Users/mdedetrich/github/slick/doc/code/SqlToSlick.scala:287: error: [dialect scala3] `)` expected but `<-` found
            p <- people58 source files formatted
              ^: /Users/mdedetrich/github/slick/doc/code/SqlToSlick.scala
```

## Expectation

I would expect scalafmt to not fail

## Workaround

Turning the formatting off with `// format: off` works

## Notes

Changing the dialect with different dialects using `fileOverride { "glob:**/doc/**.scala" = scala212 }` doesn't have any effect, so it seems unrelated to dialect.

See original PR at https://github.com/slick/slick/pull/2909

Pull request description:
Fixes scalameta/scalafmt#3911.

Patch:
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala
index 61d59f5a51..484629d3af 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala
@@ -660,6 +660,7 @@ final class ScannerTokens(val tokens: Tokens)(implicit dialect: Dialect) {
           case RegionTry :: xs
               if !next.isAny[KwCatch, KwFinally] && isLeadingInfix != LeadingInfix.Yes => strip(xs)
           case Nil | (_: CanProduceLF) :: _ => Some(rs)
+          case RegionParen :: RegionForMaybeParens :: _ => Some(rs)
           case _ => None
         }
         val ok = lastNewlinePos >= 0 && !CantStartStat(next) &&


Test patch:
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/TermSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/TermSuite.scala
index ff920994b8..52fd157356 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/TermSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/TermSuite.scala
@@ -1334,4 +1334,106 @@ class TermSuite extends ParseSuite {
     runTestAssert[Term](code, Some(layout))(tree)
   }
 
+  test("scalafmt #3911 for in parens, NL after `(`, NL between") {
+    val code = """|for (
+                  |  a <- fooa
+                  |  b <- foob
+                  |  if a === b
+                  |) yield a
+                  |""".stripMargin
+    val layout = "for (a <- fooa; b <- foob; if a === b) yield a"
+    val tree = Term.ForYield(
+      List(
+        Enumerator.Generator(Pat.Var(tname("a")), tname("fooa")),
+        Enumerator.Generator(Pat.Var(tname("b")), tname("foob")),
+        Enumerator.Guard(Term.ApplyInfix(tname("a"), tname("==="), Nil, List(tname("b"))))
+      ),
+      tname("a")
+    )
+    runTestAssert[Term](code, layout)(tree)
+  }
+
+  test("scalafmt #3911 for in parens, NL after `(`, `;` between") {
+    val code = """|for (
+                  |  a <- fooa;
+                  |  b <- foob;
+                  |  if a === b
+                  |) yield a
+                  |""".stripMargin
+    val layout = "for (a <- fooa; b <- foob; if a === b) yield a"
+    val tree = Term.ForYield(
+      List(
+        Enumerator.Generator(Pat.Var(tname("a")), tname("fooa")),
+        Enumerator.Generator(Pat.Var(tname("b")), tname("foob")),
+        Enumerator.Guard(Term.ApplyInfix(tname("a"), tname("==="), Nil, List(tname("b"))))
+      ),
+      tname("a")
+    )
+    runTestAssert[Term](code, layout)(tree)
+  }
+
+  test("scalafmt #3911 for in parens, no NL after `(`, NL between") {
+    val code = """|for (a <- fooa
+                  |     b <- foob
+                  |     if a === b) yield a
+                  |""".stripMargin
+    val layout = "for (a <- fooa; b <- foob; if a === b) yield a"
+    val tree = Term.ForYield(
+      List(
+        Enumerator.Generator(Pat.Var(tname("a")), tname("fooa")),
+        Enumerator.Generator(Pat.Var(tname("b")), tname("foob")),
+        Enumerator.Guard(Term.ApplyInfix(tname("a"), tname("==="), Nil, List(tname("b"))))
+      ),
+      tname("a")
+    )
+    runTestAssert[Term](code, layout)(tree)
+  }
+
+  test("scalafmt #3911 for in parens, no NL after `(`, `;` between") {
+    val code = """|for (a <- fooa;
+                  |     b <- foob;
+                  |     if a === b) yield a
+                  |""".stripMargin
+    val layout = "for (a <- fooa; b <- foob; if a === b) yield a"
+    val tree = Term.ForYield(
+      List(
+        Enumerator.Generator(Pat.Var(tname("a")), tname("fooa")),
+        Enumerator.Generator(Pat.Var(tname("b")), tname("foob")),
+        Enumerator.Guard(Term.ApplyInfix(tname("a"), tname("==="), Nil, List(tname("b"))))
+      ),
+      tname("a")
+    )
+    runTestAssert[Term](code, layout)(tree)
+  }
+
+  test("scalafmt #3911 for in parens, no NL after `(`, NL between, no NL before guard") {
+    val code = """|for (a <- fooa if
+                  |       a > 0) yield a
+                  |""".stripMargin
+    val layout = "for (a <- fooa; if a > 0) yield a"
+    val tree = Term.ForYield(
+      List(
+        Enumerator.Generator(Pat.Var(tname("a")), tname("fooa")),
+        Enumerator.Guard(Term.ApplyInfix(tname("a"), tname(">"), Nil, List(lit(0))))
+      ),
+      tname("a")
+    )
+    runTestAssert[Term](code, layout)(tree)
+  }
+
+  test("scalafmt #3911 for in parens, no NL after `(`, `;` between, no NL before guard op") {
+    val code = """|for (a <- fooa if a >
+                  |       0) yield a
+                  |""".stripMargin
+    val layout = "for (a <- fooa; if a > 0) yield a"
+    val tree = Term.ForYield(
+      List(
+        Enumerator.Generator(Pat.Var(tname("a")), tname("fooa")),
+        Enumerator.Guard(Term.ApplyInfix(tname("a"), tname(">"), Nil, List(lit(0))))
+      ),
+      tname("a")
+    )
+    runTestAssert[Term](code, layout)(tree)
+  }
+
 }