Skip to content

Commit 110e5cb

Browse files
authored
Merge pull request #12954 from som-snytt/issue/12340
2 parents c69da49 + 58f9a23 commit 110e5cb

File tree

8 files changed

+93
-61
lines changed

8 files changed

+93
-61
lines changed

Diff for: compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+44-49
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ object Parsers {
330330
else if in.token == END then
331331
if endSeen then syntaxError("duplicate end marker")
332332
checkEndMarker(stats)
333-
recur(sepSeen, true)
333+
recur(sepSeen, endSeen = true)
334334
else if isStatSeqEnd || in.token == altEnd then
335335
false
336336
else if sepSeen || endSeen then
@@ -537,15 +537,13 @@ object Parsers {
537537
def inBrackets[T](body: => T): T = enclosed(LBRACKET, body)
538538

539539
def inBracesOrIndented[T](body: => T, rewriteWithColon: Boolean = false): T =
540-
if (in.token == INDENT) {
541-
val rewriteToBraces =
542-
in.rewriteNoIndent &&
543-
!testChars(in.lastOffset - 3, " =>") // braces are always optional after `=>` so none should be inserted
544-
if (rewriteToBraces) indentedToBraces(body)
540+
if in.token == INDENT then
541+
val rewriteToBraces = in.rewriteNoIndent
542+
&& !testChars(in.lastOffset - 3, " =>") // braces are always optional after `=>` so none should be inserted
543+
if rewriteToBraces then indentedToBraces(body)
545544
else enclosed(INDENT, body)
546-
}
547545
else
548-
if (in.rewriteToIndent) bracesToIndented(body, rewriteWithColon)
546+
if in.rewriteToIndent then bracesToIndented(body, rewriteWithColon)
549547
else inBraces(body)
550548

551549
def inDefScopeBraces[T](body: => T, rewriteWithColon: Boolean = false): T =
@@ -635,68 +633,62 @@ object Parsers {
635633
else idx
636634

637635
/** Parse indentation region `body` and rewrite it to be in braces instead */
638-
def indentedToBraces[T](body: => T): T = {
639-
val enclRegion = in.currentRegion.enclosing
640-
def indentWidth = enclRegion.indentWidth
636+
def indentedToBraces[T](body: => T): T =
637+
val enclRegion = in.currentRegion.enclosing // capture on entry
638+
def indentWidth = enclRegion.indentWidth
641639
val followsColon = testChar(in.lastOffset - 1, ':')
642-
val startOpening =
643-
if (followsColon)
644-
if (testChar(in.lastOffset - 2, ' ')) in.lastOffset - 2
645-
else in.lastOffset - 1
646-
else in.lastOffset
647-
val endOpening = in.lastOffset
648-
649-
val t = enclosed(INDENT, body)
650640

651641
/** Is `expr` a tree that lacks a final `else`? Put such trees in `{...}` to make
652642
* sure we don't accidentally merge them with a following `else`.
653643
*/
654644
def isPartialIf(expr: Tree): Boolean = expr match {
655645
case If(_, _, EmptyTree) => true
656-
case If(_, _, e) => isPartialIf(e)
657-
case _ => false
646+
case If(_, _, e) => isPartialIf(e)
647+
case _ => false
658648
}
659649

660650
/** Is `expr` a (possibly curried) function that has a multi-statement block
661651
* as body? Put such trees in `{...}` since we don't enclose statements following
662652
* a `=>` in braces.
663653
*/
664654
def isBlockFunction[T](expr: T): Boolean = expr match {
665-
case Function(_, body) => isBlockFunction(body)
655+
case Function(_, body) => isBlockFunction(body)
666656
case Block(stats, expr) => stats.nonEmpty || isBlockFunction(expr)
667-
case _ => false
657+
case _ => false
668658
}
669659

670660
/** Start of first line after in.lastOffset that does not have a comment
671661
* at indent width greater than the indent width of the closing brace.
672662
*/
673663
def closingOffset(lineStart: Offset): Offset =
674-
if (in.lineOffset >= 0 && lineStart >= in.lineOffset) in.lineOffset
675-
else {
676-
val candidate = source.nextLine(lineStart)
664+
if in.lineOffset >= 0 && lineStart >= in.lineOffset then in.lineOffset
665+
else
677666
val commentStart = skipBlanks(lineStart)
678-
if (testChar(commentStart, '/') && indentWidth < in.indentWidth(commentStart))
679-
closingOffset(source.nextLine(lineStart))
680-
else
681-
lineStart
682-
}
667+
if testChar(commentStart, '/') && indentWidth < in.indentWidth(commentStart)
668+
then closingOffset(source.nextLine(lineStart))
669+
else lineStart
683670

684671
def needsBraces(t: Any): Boolean = t match {
685672
case Match(EmptyTree, _) => true
686-
case Block(stats, expr) =>
687-
stats.nonEmpty || needsBraces(expr)
688-
case expr: Tree =>
689-
followsColon ||
690-
isPartialIf(expr) && in.token == ELSE ||
691-
isBlockFunction(expr)
692-
case _ => true
693-
}
694-
if (needsBraces(t)) {
673+
case Block(stats, expr) => stats.nonEmpty || needsBraces(expr)
674+
case expr: Tree => followsColon
675+
|| isPartialIf(expr) && in.token == ELSE
676+
|| isBlockFunction(expr)
677+
case _ => true
678+
}
679+
// begin indentedToBraces
680+
val startOpening =
681+
if followsColon then
682+
if testChar(in.lastOffset - 2, ' ') then in.lastOffset - 2
683+
else in.lastOffset - 1
684+
else in.lastOffset
685+
val endOpening = in.lastOffset
686+
val t = enclosed(INDENT, body)
687+
if needsBraces(t) then
695688
patch(source, Span(startOpening, endOpening), " {")
696689
patch(source, Span(closingOffset(source.nextLine(in.lastOffset))), indentWidth.toPrefix ++ "}\n")
697-
}
698690
t
699-
}
691+
end indentedToBraces
700692

701693
/** The region to eliminate when replacing an opening `(` or `{` that ends a line.
702694
* The `(` or `{` is at in.offset.
@@ -1304,17 +1296,21 @@ object Parsers {
13041296
case _: (ForYield | ForDo) => in.token == FOR
13051297
case _ => false
13061298

1307-
def matchesAndSetEnd(last: T): Boolean = {
1299+
def endName = if in.token == IDENTIFIER then in.name.toString else tokenString(in.token)
1300+
1301+
def matchesAndSetEnd(last: T): Boolean =
13081302
val didMatch = matches(last)
13091303
if didMatch then
13101304
updateSpanOfLast(last)
13111305
didMatch
1312-
}
13131306

13141307
if in.token == END then
13151308
val start = in.skipToken()
13161309
if stats.isEmpty || !matchesAndSetEnd(stats.last) then
13171310
syntaxError("misaligned end marker", Span(start, in.lastCharOffset))
1311+
else if overlapsPatch(source, Span(start, start)) then
1312+
patch(source, Span(start, start), "")
1313+
patch(source, Span(start, in.lastCharOffset), s"} // end $endName")
13181314
in.token = IDENTIFIER // Leaving it as the original token can confuse newline insertion
13191315
in.nextToken()
13201316
end checkEndMarker
@@ -3854,9 +3850,9 @@ object Parsers {
38543850
def templateStatSeq(): (ValDef, List[Tree]) = checkNoEscapingPlaceholders {
38553851
var self: ValDef = EmptyValDef
38563852
val stats = new ListBuffer[Tree]
3857-
if (isExprIntro && !isDefIntro(modifierTokens)) {
3853+
if isExprIntro && !isDefIntro(modifierTokens) then
38583854
val first = expr1()
3859-
if (in.token == ARROW) {
3855+
if in.token == ARROW then
38603856
first match {
38613857
case Typed(tree @ This(EmptyTypeIdent), tpt) =>
38623858
self = makeSelfDef(nme.WILDCARD, tpt).withSpan(first.span)
@@ -3867,11 +3863,10 @@ object Parsers {
38673863
}
38683864
in.token = SELFARROW // suppresses INDENT insertion after `=>`
38693865
in.nextToken()
3870-
}
38713866
else
38723867
stats += first
38733868
statSepOrEnd(stats)
3874-
}
3869+
end if
38753870
while
38763871
var empty = false
38773872
if (in.token == IMPORT)
@@ -3888,7 +3883,7 @@ object Parsers {
38883883
empty = true
38893884
statSepOrEnd(stats, empty)
38903885
do ()
3891-
(self, if (stats.isEmpty) List(EmptyTree) else stats.toList)
3886+
(self, if stats.isEmpty then List(EmptyTree) else stats.toList)
38923887
}
38933888

38943889
/** RefineStatSeq ::= RefineStat {semi RefineStat}

Diff for: compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala

+10-10
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@ object Rewrites {
2020
private[Rewrites] val pbuf = new mutable.ListBuffer[Patch]()
2121

2222
def addPatch(span: Span, replacement: String): Unit =
23-
pbuf += Patch(span, replacement)
23+
pbuf.indexWhere(p => p.span.start == span.start && p.span.end == span.end) match {
24+
case i if i >= 0 => pbuf.update(i, Patch(span, replacement))
25+
case _ => pbuf += Patch(span, replacement)
26+
}
2427

2528
def apply(cs: Array[Char]): Array[Char] = {
2629
val delta = pbuf.map(_.delta).sum
2730
val patches = pbuf.toList.sortBy(_.span.start)
2831
if (patches.nonEmpty)
29-
patches reduceLeft {(p1, p2) =>
32+
patches.reduceLeft {(p1, p2) =>
3033
assert(p1.span.end <= p2.span.start, s"overlapping patches in $source: $p1 and $p2")
3134
p2
3235
}
@@ -64,11 +67,11 @@ object Rewrites {
6467
* given by `span` in `source` by `replacement`
6568
*/
6669
def patch(source: SourceFile, span: Span, replacement: String)(using Context): Unit =
67-
if (ctx.reporter != Reporter.NoReporter) // NoReporter is used for syntax highlighting
68-
for (rewrites <- ctx.settings.rewrite.value)
69-
rewrites.patched
70-
.getOrElseUpdate(source, new Patches(source))
71-
.addPatch(span, replacement)
70+
if ctx.reporter != Reporter.NoReporter // NoReporter is used for syntax highlighting
71+
then ctx.settings.rewrite.value.foreach(_.patched
72+
.getOrElseUpdate(source, new Patches(source))
73+
.addPatch(span, replacement)
74+
)
7275

7376
/** Patch position in `ctx.compilationUnit.source`. */
7477
def patch(span: Span, replacement: String)(using Context): Unit =
@@ -96,6 +99,3 @@ class Rewrites {
9699
import Rewrites._
97100
private val patched = new PatchedFiles
98101
}
99-
100-
101-

Diff for: compiler/src/dotty/tools/dotc/util/Spans.scala

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ object Spans {
8585
|| containsInner(this, that.end)
8686
|| containsInner(that, this.start)
8787
|| containsInner(that, this.end)
88+
|| this.start == that.start && this.end == that.end // exact match in one point
8889
)
8990
}
9091

Diff for: compiler/test/dotty/tools/dotc/CompilationTests.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ class CompilationTests {
7272
compileFile("tests/rewrites/rewrites3x.scala", defaultOptions.and("-rewrite", "-source", "future-migration")),
7373
compileFile("tests/rewrites/i8982.scala", defaultOptions.and("-indent", "-rewrite")),
7474
compileFile("tests/rewrites/i9632.scala", defaultOptions.and("-indent", "-rewrite")),
75-
compileFile("tests/rewrites/i11895.scala", defaultOptions.and("-indent", "-rewrite"))
75+
compileFile("tests/rewrites/i11895.scala", defaultOptions.and("-indent", "-rewrite")),
76+
compileFile("tests/rewrites/i12340.scala", unindentOptions.and("-rewrite")),
7677
).checkRewrites()
7778
}
7879

Diff for: compiler/test/dotty/tools/vulpix/ParallelTesting.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,7 @@ trait ParallelTesting extends RunnerOrchestration { self =>
978978
target.copy(dir = copyToDir(outDir, dir))
979979
}
980980

981-
val test = new RewriteTest(copiedTargets, checkFileMap, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
981+
new RewriteTest(copiedTargets, checkFileMap, times, threadLimit, shouldFail || shouldSuppressOutput).executeTestSuite()
982982
this
983983
}
984984

Diff for: compiler/test/dotty/tools/vulpix/TestConfiguration.scala

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ object TestConfiguration {
6060

6161
val commonOptions = Array("-indent", "-language:postfixOps") ++ checkOptions ++ noCheckOptions ++ yCheckOptions
6262
val defaultOptions = TestFlags(basicClasspath, commonOptions)
63+
val unindentOptions = TestFlags(basicClasspath, Array("-no-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions)
6364
val withCompilerOptions =
6465
defaultOptions.withClasspath(withCompilerClasspath).withRunClasspath(withCompilerClasspath)
6566
lazy val withStagingOptions =

Diff for: tests/rewrites/i12340.check

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
class C {
3+
def f = 42
4+
} // end C
5+
6+
def f(i: Int) = {
7+
if i < 42 then
8+
println(i)
9+
end if
10+
i
11+
} // end f
12+
13+
def g(i: Int) =
14+
if i < 42 then
15+
println(i)
16+
end if
17+
end g

Diff for: tests/rewrites/i12340.scala

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
class C:
3+
def f = 42
4+
end C
5+
6+
def f(i: Int) =
7+
if i < 42 then
8+
println(i)
9+
end if
10+
i
11+
end f
12+
13+
def g(i: Int) =
14+
if i < 42 then
15+
println(i)
16+
end if
17+
end g

0 commit comments

Comments
 (0)