Skip to content

Commit faf1a62

Browse files
committed
Implement sub-diagnostics
1 parent add10eb commit faf1a62

File tree

2 files changed

+54
-16
lines changed

2 files changed

+54
-16
lines changed

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import dotty.tools.dotc.util.chaining.*
1111
import java.util.{Collections, Optional, List => JList}
1212
import core.Decorators.toMessage
1313

14+
import collection.mutable.ArrayBuffer
15+
1416
object Diagnostic:
1517

1618
def shouldExplain(dia: Diagnostic)(using Context): Boolean =
@@ -117,6 +119,22 @@ class Diagnostic(
117119
msg.message.replaceAll("\u001B\\[[;\\d]*m", "")
118120
override def diagnosticRelatedInformation: JList[interfaces.DiagnosticRelatedInformation] =
119121
Collections.emptyList()
120-
121122
override def toString: String = s"$getClass at $pos L${pos.line+1}: $message"
123+
124+
private val subdiags: ArrayBuffer[Subdiagnostic] = ArrayBuffer.empty
125+
126+
def addSubdiag(diag: Subdiagnostic): Unit =
127+
subdiags += diag
128+
129+
def addSubdiag(msg: Message, pos: SourcePosition): Unit =
130+
addSubdiag(Subdiagnostic(msg, pos))
131+
132+
def withSubdiags(diags: List[Subdiagnostic]): this.type =
133+
diags.foreach(addSubdiag)
134+
this
135+
136+
def getSubdiags: List[Subdiagnostic] = subdiags.toList
122137
end Diagnostic
138+
139+
class Subdiagnostic(val msg: Message, val pos: SourcePosition)
140+

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

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,11 @@ trait MessageRendering {
7777
* -- Error: source.scala ---------------------
7878
* ```
7979
*/
80-
private def boxTitle(title: String)(using Context, Level, Offset): String =
80+
private def boxTitle(title: String, isSubtitle: Boolean = false)(using Context, Level, Offset): String =
8181
val pageWidth = ctx.settings.pageWidth.value
8282
val line = "-" * (pageWidth - title.length - 4)
83-
hl(s"-- $title $line")
83+
val starter = if isSubtitle then ".." else "--"
84+
hl(s"$starter $title $line")
8485

8586
/** The position markers aligned under the error
8687
*
@@ -169,7 +170,8 @@ trait MessageRendering {
169170
private def posStr(
170171
pos: SourcePosition,
171172
message: Message,
172-
diagnosticString: String
173+
diagnosticString: String,
174+
isSubdiag: Boolean = false
173175
)(using Context, Level, Offset): String =
174176
assert(
175177
message.errorId.isActive,
@@ -191,7 +193,7 @@ trait MessageRendering {
191193
val title =
192194
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)
193195
else s"$errId$kind: $fileAndPos"
194-
boxTitle(title)
196+
boxTitle(title, isSubtitle = isSubdiag)
195197
})
196198
else ""
197199
end posStr
@@ -232,6 +234,18 @@ trait MessageRendering {
232234
if origin.nonEmpty then
233235
addHelp("origin=")(origin)
234236

237+
// adjust a pos at EOF if preceded by newline
238+
private def adjust(pos: SourcePosition): SourcePosition =
239+
if pos.span.isSynthetic
240+
&& pos.span.isZeroExtent
241+
&& pos.span.exists
242+
&& pos.span.start == pos.source.length
243+
&& pos.source(pos.span.start - 1) == '\n'
244+
then
245+
pos.withSpan(pos.span.shift(-1))
246+
else
247+
pos
248+
235249
/** The whole message rendered from `dia.msg`.
236250
*
237251
* For a position in an inline expansion, choose `pos1`
@@ -252,17 +266,6 @@ trait MessageRendering {
252266
*
253267
*/
254268
def messageAndPos(dia: Diagnostic)(using Context): String =
255-
// adjust a pos at EOF if preceded by newline
256-
def adjust(pos: SourcePosition): SourcePosition =
257-
if pos.span.isSynthetic
258-
&& pos.span.isZeroExtent
259-
&& pos.span.exists
260-
&& pos.span.start == pos.source.length
261-
&& pos.source(pos.span.start - 1) == '\n'
262-
then
263-
pos.withSpan(pos.span.shift(-1))
264-
else
265-
pos
266269
val msg = dia.msg
267270
val pos = dia.pos
268271
val pos1 = adjust(pos.nonInlined) // innermost pos contained by call.pos
@@ -296,6 +299,9 @@ trait MessageRendering {
296299
sb.append(EOL).append(endBox)
297300
end if
298301
else sb.append(msg.message)
302+
303+
dia.getSubdiags.foreach(addSubdiagnostic(sb, _))
304+
299305
if dia.isVerbose then
300306
appendFilterHelp(dia, sb)
301307

@@ -313,6 +319,20 @@ trait MessageRendering {
313319
sb.toString
314320
end messageAndPos
315321

322+
private def addSubdiagnostic(sb: StringBuilder, subdiag: Subdiagnostic)(using Context, Level, Offset): Unit =
323+
val pos1 = adjust(subdiag.pos)
324+
val msg = subdiag.msg
325+
assert(pos1.exists && pos1.source.file.exists)
326+
327+
val posString = posStr(pos1, msg, "Note", isSubdiag = true)
328+
val (srcBefore, srcAfter, offset) = sourceLines(pos1)
329+
val marker = positionMarker(pos1)
330+
val err = errorMsg(pos1, msg.message)
331+
332+
val diagText = (posString :: srcBefore ::: marker :: err :: srcAfter).mkString(EOL)
333+
sb.append(EOL)
334+
sb.append(diagText)
335+
316336
private def hl(str: String)(using Context, Level): String =
317337
summon[Level].value match
318338
case interfaces.Diagnostic.ERROR => Red(str).show

0 commit comments

Comments
 (0)