Skip to content

Commit f02de95

Browse files
committed
Support multi-parts on the same line
1 parent aee8019 commit f02de95

File tree

3 files changed

+86
-1
lines changed

3 files changed

+86
-1
lines changed

compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,56 @@ trait MessageRendering {
332332
val partsOnLine = validParts.filter(_.srcPos.startLine == lineNum)
333333
.sortBy(p => (p.srcPos.startColumn, !p.isPrimary))
334334

335-
for part <- partsOnLine do
335+
if partsOnLine.size == 1 then
336+
// Single marker on this line
337+
val part = partsOnLine.head
336338
val markerChar = if part.isPrimary then '^' else '-'
337339
val marker = positionMarker(part.srcPos, markerChar)
338340
val err = errorMsg(part.srcPos, part.text)
339341
sb.append(marker).append(EOL)
340342
sb.append(err).append(EOL)
343+
else if partsOnLine.size > 1 then
344+
// Multiple markers on same line
345+
val markerLine = StringBuilder()
346+
markerLine.append(offsetBox)
347+
348+
var currentCol = 0
349+
for part <- partsOnLine do
350+
val markerChar = if part.isPrimary then '^' else '-'
351+
val targetCol = part.srcPos.startColumn
352+
val padding = " " * (targetCol - currentCol)
353+
markerLine.append(padding).append(markerChar)
354+
currentCol = targetCol + 1
355+
356+
sb.append(markerLine).append(EOL)
357+
358+
// Render messages from right to left with connector bars
359+
val sortedByColumn = partsOnLine.reverse // rightmost first
360+
for (part, idx) <- sortedByColumn.zipWithIndex do
361+
val remainingParts = sortedByColumn.drop(idx + 1) // parts still waiting for messages
362+
363+
// Build connector line with vertical bars for remaining parts
364+
val connectorLine = StringBuilder()
365+
connectorLine.append(offsetBox)
366+
367+
var col = 0
368+
// First, add vertical bars for all remaining (not-yet-shown) parts
369+
for p <- partsOnLine do
370+
if remainingParts.contains(p) then
371+
val targetCol = p.srcPos.startColumn
372+
val padding = " " * (targetCol - col)
373+
connectorLine.append(padding).append("|")
374+
col = targetCol + 1
375+
376+
// Then add the message for the current part, aligned to its column
377+
val msgText = part.text
378+
val msgCol = part.srcPos.startColumn
379+
// If we've added bars, col is position after last bar; if not, col is 0
380+
// We want the message to start at msgCol, with at least one space separation
381+
val msgPadding = if col == 0 then " " * msgCol else " " * Math.max(1, msgCol - col)
382+
connectorLine.append(msgPadding).append(msgText)
383+
384+
sb.append(connectorLine).append(EOL)
341385

342386
// Add explanation if needed
343387
if Diagnostic.shouldExplain(dia) then
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
-- Type Error: tests/neg-custom-args/captures/consume-twice-same-line.scala:5:16 ---------------------------------------
2+
Separation failure: Illegal access to (x : Object^), which was passed to a
3+
consume parameter or was used as a prefix to a consume method
4+
and therefore is no longer available.
5+
6+
where: ^ refers to a fresh root capability in the type of parameter x
7+
5 | send(x); send(x) // error
8+
| - ^
9+
| | Then, it was used here
10+
| The capability was consumed here.
11+
12+
-- Type Error: tests/neg-custom-args/captures/consume-twice-same-line.scala:8:16 ---------------------------------------
13+
Separation failure: Illegal access to (x : Object^), which was passed to a
14+
consume parameter or was used as a prefix to a consume method
15+
and therefore is no longer available.
16+
17+
where: ^ refers to a fresh root capability in the type of parameter x
18+
8 | send(x); send(x); send(x) // error // error
19+
| - ^
20+
| | Then, it was used here
21+
| The capability was consumed here.
22+
23+
-- Type Error: tests/neg-custom-args/captures/consume-twice-same-line.scala:8:25 ---------------------------------------
24+
Separation failure: Illegal access to (x : Object^), which was passed to a
25+
consume parameter or was used as a prefix to a consume method
26+
and therefore is no longer available.
27+
28+
where: ^ refers to a fresh root capability in the type of parameter x
29+
8 | send(x); send(x); send(x) // error // error
30+
| - ^
31+
| | Then, it was used here
32+
| The capability was consumed here.
33+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import language.experimental.captureChecking
2+
import language.experimental.separationChecking
3+
def send(consume x: Object^): Unit = ()
4+
def consumeTwice(consume x: Object^): Unit =
5+
send(x); send(x) // error
6+
7+
def consumeThrice(consume x: Object^): Unit =
8+
send(x); send(x); send(x) // error // error

0 commit comments

Comments
 (0)