Repository: scalameta/scalameta (scala)

Issue:
Scalafmt fails on manual type ascription after a `{}` block
## Configuration (required) ##

Please paste the contents of your `.scalafmt.conf` file here:
```
version                                  = 3.6.1
runner.dialect                           = scala213
project.git                              = true
style                                    = defaultWithAlign
docstrings.style                         = Asterisk
docstrings.wrap                          = false
indentOperator.preset                    = spray
maxColumn                                = 120
lineEndings                              = preserve
rewrite.rules                            = [RedundantParens, SortImports, AvoidInfix]
indentOperator.exemptScope               = all
align.preset                             = some
align.tokens."+"                         = [
  {
    code   = "~>"
    owners = [
      { regex = "Term.ApplyInfix" }
    ]
  }
]
literals.hexDigits                       = upper
literals.hexPrefix                       = lower
binPack.unsafeCallSite                   = always
binPack.unsafeDefnSite                   = always
binPack.indentCallSiteSingleArg          = false
binPack.indentCallSiteOnce               = true
newlines.avoidForSimpleOverflow          = [slc]
newlines.source                          = keep
newlines.beforeMultiline                 = keep
align.openParenDefnSite                  = false
align.openParenCallSite                  = false
align.allowOverflow                      = true
optIn.breakChainOnFirstMethodDot         = false
optIn.configStyleArguments               = false
danglingParentheses.preset               = false
spaces.inImportCurlyBraces               = true
rewrite.neverInfix.excludeFilters        = [
  and
  min
  max
  until
  to
  by
  eq
  ne
  "should.*"
  "contain.*"
  "must.*"
  in
  ignore
  be
  taggedAs
  thrownBy
  synchronized
  have
  when
  size
  only
  noneOf
  oneElementOf
  noElementsOf
  atLeastOneElementOf
  atMostOneElementOf
  allElementsOf
  inOrderElementsOf
  theSameElementsAs
  theSameElementsInOrderAs
]
rewriteTokens          = {
  "⇒": "=>"
  "→": "->"
  "←": "<-"
}
project.excludeFilters = [
  "scripts/authors.scala"
]
project.layout         = StandardConvention
```

## Command-line parameters (required) ##

When I run scalafmt via CLI like this: `scalafmt`

## Steps

Given code like this:
```scala
        implicit val um: Unmarshaller[HttpResponse, StorageObject] = Unmarshaller.withMaterializer {
          implicit ec => implicit mat =>
            {
              case HttpResponse(status, _, entity, _) if status.isSuccess() =>
                Unmarshal(entity).to[StorageObject]
              case HttpResponse(status, _, entity, _) =>
                Unmarshal(entity).to[String].flatMap { errorString =>
                  Future.failed(new RuntimeException(s"Uploading part failed with status $status: $errorString"))
                }
            }: PartialFunction[HttpResponse, Future[StorageObject]]
        }.withDefaultRetry
```

## Problem

Scalafmt errors with the following message
```scala
org.scalafmt.dynamic.exceptions.PositionExceptionImpl: /Users/mdedetrich/github/incubator-pekko-connectors/google-cloud-storage/src/main/scala/akka/stream/alpakka/googlecloud/storage/impl/GCStorageStream.scala:158: error: [dialect scala213] ; expected but : found
            }: PartialFunction[HttpResponse, Future[StorageObject]]
             ^: /Users/mdedetrich/github/incubator-pekko-connectors/google-cloud-storage/src/main/scala/akka/stream/alpakka/googlecloud/storage/impl/GCStorageStream.scala
```

## Expectation

scalafmt should not error out

## Workaround

Removing the type ascription (i.e. `: PartialFunction[HttpResponse, Future[StorageObject]]`) solves the problem.

## Notes

Original problematic source file is at https://github.com/apache/incubator-pekko-connectors/blob/4ebf24a4f996b088b4ed16cee182accc5ac92e1c/google-cloud-storage/src/main/scala/akka/stream/alpakka/googlecloud/storage/impl/GCStorageStream.scala#L149-L159

Pull request:
Fixes #2959.

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 c35322f8fe..fa42bf8011 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
@@ -1802,12 +1802,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       Term.Param(List(atPos(implicitPos)(Mod.Implicit())), paramName, paramTpt, None)
     )
     accept[RightArrow]
-    autoEndPos(implicitPos)(
-      Term.Function(
-        List(param),
-        if (location != BlockStat) expr() else blockExpr(isBlockOptional = true)
-      )
-    )
+    autoEndPos(implicitPos)(Term.Function(List(param), expr(location, allowRepeated = false)))
   }
 
   // Encapsulates state and behavior of parsing infix syntax.


Test patch:
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 a9f7c7fac5..6a04f37b13 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
@@ -173,6 +173,15 @@ class TokensPositionSuite extends BasePositionSuite(dialects.Scala213) {
        |Case case a => a
        |""".stripMargin
   )
+  checkPositions[Stat](
+    "foo { implicit a => { b }: C }",
+    """|Term.Block { implicit a => { b }: C }
+       |Term.Function implicit a => { b }: C
+       |Term.Param implicit a
+       |Term.Ascribe { b }: C
+       |Term.Block { b }
+       |""".stripMargin
+  )
   checkPositions[Stat](
     "(f)({ x })",
     """|Term.ArgClause ({ x })
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 1f5a0e89bf..00c330432a 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
@@ -1437,4 +1437,35 @@ class TermSuite extends ParseSuite {
     }
   }
 
+  test("implicit closure with ascribe") {
+    val code = """|foo {
+                  |  implicit a => implicit b => {
+                  |    case bar => baz
+                  |  }: qux
+                  |}
+                  |""".stripMargin
+    checkTerm(code) {
+      Term.Apply(
+        Term.Name("foo"),
+        Term.ArgClause(
+          Term.Block(
+            Term.Function(
+              List(Term.Param(List(Mod.Implicit()), Term.Name("a"), None, None)),
+              Term.Function(
+                List(Term.Param(List(Mod.Implicit()), Term.Name("b"), None, None)),
+                Term.Ascribe(
+                  Term.PartialFunction(
+                    List(Case(Pat.Var(Term.Name("bar")), None, Term.Name("baz")))
+                  ),
+                  Type.Name("qux")
+                )
+              )
+            ) :: Nil
+          ) :: Nil,
+          None
+        )
+      )
+    }
+  }
+
 }