Skip to content

Commit fc0cb0d

Browse files
committed
[Scala 3] Allow return statement to contain indented block
Previously, return could not start an indentation, but starting with scala/scala3#11752 it can. This fixes scalameta to parse accordingly. Additionally, this change also created some issues with empty `return` statements in pattern matches. We would incorrectly think that the next statement is indented, because we never saved case block indentation. I added a new CaseRegion in this PR, so that we can remember the indentation. This is only added in `match` with braces as indented match clauses work correctly here.
1 parent 05999e8 commit fc0cb0d

File tree

3 files changed

+242
-11
lines changed

3 files changed

+242
-11
lines changed

community-test/src/test/scala/CommunityDottySuite.scala

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,15 @@ class CommunityDottySuite extends FunSuite {
6060
val communityBuilds = List(
6161
CommunityBuild(
6262
"https://github.com/lampepfl/dotty.git",
63-
//commit hash from 17.02.2021
64-
"8bbb0ba745453a0d2ffdb94c5966752dfe57bb5f",
63+
//commit hash from 31.03.2021
64+
"13de192d04ae3e4beeaa8257c1f6dde0d2e81350",
6565
"dotty",
6666
dottyExclusionList
6767
),
6868
CommunityBuild(
6969
"https://github.com/scalameta/munit.git",
70-
// latest commit from 16.02.2021
71-
"bf6fd2294decdd89f887d47461f12af8f6083c5a",
70+
// latest commit from 30.03.2021
71+
"06346adfe3519c384201eec531762dad2f4843dc",
7272
"munit",
7373
munitExclusionList
7474
)
@@ -154,10 +154,7 @@ class CommunityDottySuite extends FunSuite {
154154
build.excluded.exists(el => path.endsWith(el))
155155
}
156156

157-
final def dottyExclusionList = List(
158-
// [scalameta] erased modifier - for now used internally, will be available in 3.1
159-
"library/src/scala/compiletime/package.scala"
160-
)
157+
final def dottyExclusionList = List()
161158

162159
final def munitExclusionList = List(
163160
// xml literals are longer valid in Scala 3
@@ -166,6 +163,7 @@ class CommunityDottySuite extends FunSuite {
166163

167164
final val ignoreParts = List(
168165
"/tests/",
166+
"/test-resources/scripting/",
169167
"/sbt-test/",
170168
"/out/",
171169
"/language-server/src/dotty/"

scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
223223
case object RegionBracket extends SepRegion
224224
case class RegionBrace(override val indent: Int, override val indentOnArrow: Boolean)
225225
extends SepRegion
226+
case class RegionCase(override val indent: Int) extends SepRegion
226227
case class RegionEnum(override val indent: Int) extends SepRegion
227228
case class RegionIndentEnum(override val indent: Int) extends SepRegion {
228229
override def isIndented: Boolean = true
@@ -441,6 +442,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
441442
in match {
442443
case x :: xs if x.isIndented && !isBraceOrEnum(x) =>
443444
(xs, mkOutdent(currPos))
445+
// } in case ends both case region and match brace region
446+
case (x1: RegionCase) :: x2 :: xs if isBraceOrEnum(x2) =>
447+
(xs, currRef)
444448
case x :: xs if isBraceOrEnum(x) =>
445449
(xs, currRef)
446450
case x :: xs =>
@@ -486,8 +490,17 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
486490
(nextRegions, currRef)
487491
} else if (curr.is[RightArrow]) {
488492
val nextRegions =
489-
if (!sepRegions.isEmpty && sepRegions.head == RegionArrow) sepRegions.tail
490-
else sepRegions
493+
if (sepRegions.nonEmpty && sepRegions.head == RegionArrow) {
494+
val newRegions = sepRegions.tail
495+
val shouldNotProduceIndentation =
496+
!dialect.allowSignificantIndentation ||
497+
newRegions.headOption.exists(!_.indentOnArrow)
498+
lazy val indentInCase = if (isAheadNewLine(currPos)) countIndent(nextPos) else -1
499+
if (newRegions.nonEmpty && shouldNotProduceIndentation && indentInCase > 0)
500+
RegionCase(indentInCase) :: newRegions
501+
else
502+
newRegions
503+
} else sepRegions
491504
(nextRegions, currRef)
492505
} else (sepRegions, currRef)
493506
} else {
@@ -518,6 +531,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
518531
next != null && next.isNot[CantStartStat] &&
519532
(sepRegions.isEmpty ||
520533
sepRegions.head.isInstanceOf[RegionBrace] ||
534+
sepRegions.head.isInstanceOf[RegionCase] ||
521535
sepRegions.head.isInstanceOf[RegionEnum] ||
522536
sepRegions.head.isInstanceOf[RegionIndent] ||
523537
sepRegions.head.isInstanceOf[RegionIndentEnum])
@@ -1145,7 +1159,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
11451159
token.is[KwYield] || token.is[KwTry] || token.is[KwCatch] || token.is[KwFinally] ||
11461160
token.is[KwMatch] || token.is[KwDo] || token.is[KwFor] || token.is[KwThen] ||
11471161
token.is[KwElse] || token.is[Equals] || token.is[KwWhile] || token.is[KwIf] ||
1148-
token.is[RightArrow] || (token.is[KwWith] && token.next.is[DclIntro])
1162+
token.is[RightArrow] || token.is[KwReturn] || (token.is[KwWith] && token.next.is[DclIntro])
11491163
}
11501164
}
11511165

tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/SignificantIndentationSuite.scala

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,4 +1143,223 @@ class SignificantIndentationSuite extends BaseDottySuite {
11431143
)
11441144

11451145
}
1146+
1147+
test("return-indent") {
1148+
runTestAssert[Stat](
1149+
"""|def method =
1150+
| return
1151+
| val a = 2 + 3
1152+
| a
1153+
|
1154+
|""".stripMargin,
1155+
assertLayout = Some(
1156+
"""|def method = return {
1157+
| val a = 2 + 3
1158+
| a
1159+
|}
1160+
|""".stripMargin
1161+
)
1162+
)(
1163+
Defn.Def(
1164+
Nil,
1165+
Term.Name("method"),
1166+
Nil,
1167+
Nil,
1168+
None,
1169+
Term.Return(
1170+
Term.Block(
1171+
List(
1172+
Defn.Val(
1173+
Nil,
1174+
List(Pat.Var(Term.Name("a"))),
1175+
None,
1176+
Term.ApplyInfix(Lit.Int(2), Term.Name("+"), Nil, List(Lit.Int(3)))
1177+
),
1178+
Term.Name("a")
1179+
)
1180+
)
1181+
)
1182+
)
1183+
)
1184+
}
1185+
1186+
test("return-single-indent") {
1187+
runTestAssert[Stat](
1188+
"""|def method =
1189+
| return
1190+
| 2
1191+
| + 3
1192+
|""".stripMargin,
1193+
assertLayout = Some(
1194+
"def method = return 2 + 3"
1195+
)
1196+
)(
1197+
Defn.Def(
1198+
Nil,
1199+
Term.Name("method"),
1200+
Nil,
1201+
Nil,
1202+
None,
1203+
Term.Return(
1204+
Term.ApplyInfix(Lit.Int(2), Term.Name("+"), Nil, List(Lit.Int(3)))
1205+
)
1206+
)
1207+
)
1208+
}
1209+
1210+
test("empty-return") {
1211+
runTestAssert[Stat](
1212+
"""| def skip = {
1213+
| token match {
1214+
| case RBRACE =>
1215+
| if (true)
1216+
| return
1217+
| change(-1)
1218+
| }
1219+
| }
1220+
|""".stripMargin,
1221+
assertLayout = Some(
1222+
"""|def skip = {
1223+
| token match {
1224+
| case RBRACE =>
1225+
| if (true) return
1226+
| change(-1)
1227+
| }
1228+
|}
1229+
|""".stripMargin
1230+
)
1231+
)(
1232+
Defn.Def(
1233+
Nil,
1234+
Term.Name("skip"),
1235+
Nil,
1236+
Nil,
1237+
None,
1238+
Term.Block(
1239+
List(
1240+
Term.Match(
1241+
Term.Name("token"),
1242+
List(
1243+
Case(
1244+
Term.Name("RBRACE"),
1245+
None,
1246+
Term.Block(
1247+
List(
1248+
Term.If(Lit.Boolean(true), Term.Return(Lit.Unit()), Lit.Unit(), Nil),
1249+
Term.Apply(Term.Name("change"), List(Lit.Int(-1)))
1250+
)
1251+
)
1252+
)
1253+
),
1254+
Nil
1255+
)
1256+
)
1257+
)
1258+
)
1259+
)
1260+
}
1261+
1262+
test("case-block") {
1263+
runTestAssert[Stat](
1264+
"""|val success = suffixes.find { suffix =>
1265+
| try {
1266+
| true
1267+
| } catch {
1268+
| case e: StorageException =>
1269+
| false
1270+
| }
1271+
|}
1272+
|""".stripMargin,
1273+
assertLayout = Some(
1274+
"""|val success = suffixes.find {
1275+
| suffix => try {
1276+
| true
1277+
| } catch {
1278+
| case e: StorageException => false
1279+
| }
1280+
|}
1281+
|""".stripMargin
1282+
)
1283+
)(
1284+
Defn.Val(
1285+
Nil,
1286+
List(Pat.Var(Term.Name("success"))),
1287+
None,
1288+
Term.Apply(
1289+
Term.Select(Term.Name("suffixes"), Term.Name("find")),
1290+
List(
1291+
Term.Block(
1292+
List(
1293+
Term.Function(
1294+
List(Term.Param(Nil, Term.Name("suffix"), None, None)),
1295+
Term.Try(
1296+
Term.Block(List(Lit.Boolean(true))),
1297+
List(
1298+
Case(
1299+
Pat.Typed(Pat.Var(Term.Name("e")), Type.Name("StorageException")),
1300+
None,
1301+
Lit.Boolean(false)
1302+
)
1303+
),
1304+
None
1305+
)
1306+
)
1307+
)
1308+
)
1309+
)
1310+
)
1311+
)
1312+
)
1313+
}
1314+
1315+
test("complext-match-else") {
1316+
runTestAssert[Stat](
1317+
"""|val calleeType = a match {
1318+
| case _ =>
1319+
| if cond then
1320+
| expr match
1321+
| case _ =>
1322+
| f
1323+
| else NoType
1324+
| case _ =>
1325+
| NoType
1326+
|}
1327+
|""".stripMargin,
1328+
assertLayout = Some(
1329+
"""|val calleeType = a match {
1330+
| case _ =>
1331+
| if (cond) expr match {
1332+
| case _ => f
1333+
| } else NoType
1334+
| case _ =>
1335+
| NoType
1336+
|}
1337+
|""".stripMargin
1338+
)
1339+
)(
1340+
Defn.Val(
1341+
Nil,
1342+
List(Pat.Var(Term.Name("calleeType"))),
1343+
None,
1344+
Term.Match(
1345+
Term.Name("a"),
1346+
List(
1347+
Case(
1348+
Pat.Wildcard(),
1349+
None,
1350+
Term.If(
1351+
Term.Name("cond"),
1352+
Term
1353+
.Match(Term.Name("expr"), List(Case(Pat.Wildcard(), None, Term.Name("f"))), Nil),
1354+
Term.Name("NoType"),
1355+
Nil
1356+
)
1357+
),
1358+
Case(Pat.Wildcard(), None, Term.Name("NoType"))
1359+
),
1360+
Nil
1361+
)
1362+
)
1363+
)
1364+
}
11461365
}

0 commit comments

Comments
 (0)