Skip to content

Commit 05e04f0

Browse files
committed
Multi-span error reporting for consume errors
1 parent c6c6426 commit 05e04f0

File tree

3 files changed

+18
-5
lines changed

3 files changed

+18
-5
lines changed

compiler/src/dotty/tools/dotc/cc/SepCheck.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,8 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
449449
|and therefore is no longer available.""",
450450
useLoc.sourcePos
451451
):
452-
addSubdiag(em"... $ref was consumed here.", consumedLoc.sourcePos)
452+
addSubdiag(em"$ref was consumed here.", consumedLoc.sourcePos)
453+
addPrimaryNote(em"... and it was used here")
453454

454455
/** Report a failure where a previously consumed capability is used again,
455456
* @param ref the capability that is used after being consumed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,18 @@ class Diagnostic(
123123

124124
private val subdiags: ArrayBuffer[Subdiagnostic] = ArrayBuffer.empty
125125

126+
private var primaryNote: Message | Null = null
127+
126128
def addSubdiag(diag: Subdiagnostic): Unit =
127129
subdiags += diag
128130

131+
def addPrimaryNote(msg: Message): Unit =
132+
assert(primaryNote eq null)
133+
primaryNote = msg
134+
135+
def getPrimaryNote: Option[Message] =
136+
if primaryNote eq null then None else Some(primaryNote.nn)
137+
129138
def addSubdiag(msg: Message, pos: SourcePosition): Unit =
130139
addSubdiag(Subdiagnostic(msg, pos))
131140

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ trait MessageRendering {
313313
val posString = posStr(pos1, msg, diagnosticLevel(dia))
314314
if posString.nonEmpty then sb.append(posString).append(EOL)
315315

316-
// Display primary error message before code snippet
316+
// Always display primary error message before code snippet
317317
sb.append(msg.message)
318318
if !msg.message.endsWith(EOL) then sb.append(EOL)
319319

@@ -342,7 +342,7 @@ trait MessageRendering {
342342
val lineNumberWidth = maxLineNumber.toString.length
343343

344344
// Render each line with its markers and messages
345-
for (lineNum <- minLine to maxLine) {
345+
for (lineNum <- minLine to maxLine) do
346346
val lineIdx = lineNum - minLine
347347
if lineIdx < lines.length then
348348
val lineContent = lines(lineIdx)
@@ -360,10 +360,13 @@ trait MessageRendering {
360360
// Use '^' for primary error, '-' for sub-diagnostics
361361
val markerChar = if posAndMsg.isPrimary then '^' else '-'
362362
val marker = positionMarker(posAndMsg.pos, markerChar)
363-
val err = errorMsg(posAndMsg.pos, posAndMsg.msg.message)
363+
// For primary position: use PrimaryNote if available, otherwise use primary message
364+
val messageToShow =
365+
if posAndMsg.isPrimary then dia.getPrimaryNote.map(_.message).getOrElse(posAndMsg.msg.message)
366+
else posAndMsg.msg.message
367+
val err = errorMsg(posAndMsg.pos, messageToShow)
364368
sb.append(marker).append(EOL)
365369
sb.append(err).append(EOL)
366-
}
367370

368371
// Add explanation if needed
369372
if Diagnostic.shouldExplain(dia) then

0 commit comments

Comments
 (0)