Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions compiler/src/dotty/tools/dotc/cc/SepCheck.scala
Original file line number Diff line number Diff line change
Expand Up @@ -443,17 +443,21 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser:
|No clashing definitions were found. This might point to an internal error.""",
tree.srcPos)

class UseAfterConsume(ref: Capability, consumedLoc: SrcPos, useLoc: SrcPos)(using Context) extends reporting.Diagnostic.Error(
em"""Separation failure: Illegal access to $ref, which was passed to a
|consume parameter or was used as a prefix to a consume method
|and therefore is no longer available.""",
useLoc.sourcePos
):
addSubdiag(em"... $ref was consumed here.", consumedLoc.sourcePos)

/** Report a failure where a previously consumed capability is used again,
* @param ref the capability that is used after being consumed
* @param loc the position where the capability was consumed
* @param pos the position where the capability was used again
*/
def consumeError(ref: Capability, loc: SrcPos, pos: SrcPos)(using Context): Unit =
report.error(
em"""Separation failure: Illegal access to $ref, which was passed to a
|consume parameter or was used as a prefix to a consume method on line ${loc.line + 1}
|and therefore is no longer available.""",
pos)
ctx.reporter.report(UseAfterConsume(ref, loc, pos))

/** Report a failure where a capability is consumed in a loop.
* @param ref the capability
Expand Down
20 changes: 19 additions & 1 deletion compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import dotty.tools.dotc.util.chaining.*
import java.util.{Collections, Optional, List => JList}
import core.Decorators.toMessage

import collection.mutable.ArrayBuffer

object Diagnostic:

def shouldExplain(dia: Diagnostic)(using Context): Boolean =
Expand Down Expand Up @@ -117,6 +119,22 @@ class Diagnostic(
msg.message.replaceAll("\u001B\\[[;\\d]*m", "")
override def diagnosticRelatedInformation: JList[interfaces.DiagnosticRelatedInformation] =
Collections.emptyList()

override def toString: String = s"$getClass at $pos L${pos.line+1}: $message"

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

def addSubdiag(diag: Subdiagnostic): Unit =
subdiags += diag

def addSubdiag(msg: Message, pos: SourcePosition): Unit =
addSubdiag(Subdiagnostic(msg, pos))

def withSubdiags(diags: List[Subdiagnostic]): this.type =
diags.foreach(addSubdiag)
this

def getSubdiags: List[Subdiagnostic] = subdiags.toList
end Diagnostic

class Subdiagnostic(val msg: Message, val pos: SourcePosition)

50 changes: 35 additions & 15 deletions compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ trait MessageRendering {
* -- Error: source.scala ---------------------
* ```
*/
private def boxTitle(title: String)(using Context, Level, Offset): String =
private def boxTitle(title: String, isSubtitle: Boolean = false)(using Context, Level, Offset): String =
val pageWidth = ctx.settings.pageWidth.value
val line = "-" * (pageWidth - title.length - 4)
hl(s"-- $title $line")
val starter = if isSubtitle then ".." else "--"
hl(s"$starter $title $line")

/** The position markers aligned under the error
*
Expand Down Expand Up @@ -169,7 +170,8 @@ trait MessageRendering {
private def posStr(
pos: SourcePosition,
message: Message,
diagnosticString: String
diagnosticString: String,
isSubdiag: Boolean = false
)(using Context, Level, Offset): String =
assert(
message.errorId.isActive,
Expand All @@ -191,7 +193,7 @@ trait MessageRendering {
val title =
if fileAndPos.isEmpty then s"$errId$kind:" // this happens in dotty.tools.repl.ScriptedTests // TODO add name of source or remove `:` (and update test files)
else s"$errId$kind: $fileAndPos"
boxTitle(title)
boxTitle(title, isSubtitle = isSubdiag)
})
else ""
end posStr
Expand Down Expand Up @@ -232,6 +234,18 @@ trait MessageRendering {
if origin.nonEmpty then
addHelp("origin=")(origin)

// adjust a pos at EOF if preceded by newline
private def adjust(pos: SourcePosition): SourcePosition =
if pos.span.isSynthetic
&& pos.span.isZeroExtent
&& pos.span.exists
&& pos.span.start == pos.source.length
&& pos.source(pos.span.start - 1) == '\n'
then
pos.withSpan(pos.span.shift(-1))
else
pos

/** The whole message rendered from `dia.msg`.
*
* For a position in an inline expansion, choose `pos1`
Expand All @@ -252,17 +266,6 @@ trait MessageRendering {
*
*/
def messageAndPos(dia: Diagnostic)(using Context): String =
// adjust a pos at EOF if preceded by newline
def adjust(pos: SourcePosition): SourcePosition =
if pos.span.isSynthetic
&& pos.span.isZeroExtent
&& pos.span.exists
&& pos.span.start == pos.source.length
&& pos.source(pos.span.start - 1) == '\n'
then
pos.withSpan(pos.span.shift(-1))
else
pos
val msg = dia.msg
val pos = dia.pos
val pos1 = adjust(pos.nonInlined) // innermost pos contained by call.pos
Expand Down Expand Up @@ -296,6 +299,9 @@ trait MessageRendering {
sb.append(EOL).append(endBox)
end if
else sb.append(msg.message)

dia.getSubdiags.foreach(addSubdiagnostic(sb, _))

if dia.isVerbose then
appendFilterHelp(dia, sb)

Expand All @@ -313,6 +319,20 @@ trait MessageRendering {
sb.toString
end messageAndPos

private def addSubdiagnostic(sb: StringBuilder, subdiag: Subdiagnostic)(using Context, Level, Offset): Unit =
val pos1 = adjust(subdiag.pos)
val msg = subdiag.msg
assert(pos1.exists && pos1.source.file.exists)

val posString = posStr(pos1, msg, "Note", isSubdiag = true)
val (srcBefore, srcAfter, offset) = sourceLines(pos1)
val marker = positionMarker(pos1)
val err = errorMsg(pos1, msg.message)

val diagText = (posString :: srcBefore ::: marker :: err :: srcAfter).mkString(EOL)
sb.append(EOL)
sb.append(diagText)

private def hl(str: String)(using Context, Level): String =
summon[Level].value match
case interfaces.Diagnostic.ERROR => Red(str).show
Expand Down
Loading