dotty.tools.dotc.transform.init.Trace.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala3-compiler_3 Show documentation
Show all versions of scala3-compiler_3 Show documentation
scala3-compiler-bootstrapped
package dotty.tools.dotc
package transform
package init
import core.*
import Contexts.*
import ast.tpd.*
import util.SourcePosition
import Decorators.*, printing.SyntaxHighlighting
import scala.collection.mutable
/** Logic related to evaluation trace for showing friendly error messages
*
* A trace is a sequence of program positions which tells the evaluation order
* that leads to an error. It is usually more informative than the stack trace
* by tracking the exact sub-expression in the trace instead of only methods.
*/
object Trace:
opaque type Trace = Vector[Tree]
val empty: Trace = Vector.empty
val EMPTY_PADDING = " "
val CONNECTING_INDENT = "\u2502 " // "| "
val CHILD = "\u251c\u2500\u2500 " // "|-- "
val LAST_CHILD = "\u2514\u2500\u2500 " // "\-- "
extension (trace: Trace)
def add(node: Tree): Trace = trace :+ node
def toVector: Vector[Tree] = trace
def ++(trace2: Trace): Trace = trace ++ trace2
def show(using trace: Trace, ctx: Context): String = buildStacktrace(trace, "Calling trace:" + System.lineSeparator())
def position(using trace: Trace): Tree = trace.last
def trace(using t: Trace): Trace = t
inline def withTrace[T](t: Trace)(op: Trace ?=> T): T = op(using t)
inline def extendTrace[T](node: Tree)(using t: Trace)(op: Trace ?=> T): T = op(using t.add(node))
def buildStacktrace(trace: Trace, preamble: String)(using Context): String = if trace.isEmpty then "" else preamble + {
var lastLineNum = -1
var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer
trace.foreach { tree =>
val isLastTraceItem = tree `eq` trace.last
val pos = tree.sourcePos
val line =
if pos.source.exists then
val loc = "[ " + pos.source.file.name + ":" + (pos.line + 1) + " ]"
val code = SyntaxHighlighting.highlight(pos.lineContent.trim.nn)
i"$code\t$loc"
else
tree match
case defDef: DefTree =>
// The definition can be huge, avoid printing the whole definition.
defDef.symbol.showFullName
case _ =>
tree.show.split(System.lineSeparator(), 2).nn.head.nn
val positionMarkerLine =
if pos.exists && pos.source.exists then
(if isLastTraceItem then EMPTY_PADDING else CONNECTING_INDENT)+ positionMarker(pos)
else
""
// always use the more precise trace location
if lastLineNum >= 0 && lastLineNum == pos.line then
lines.dropRightInPlace(1)
val prefix = if isLastTraceItem then LAST_CHILD else CHILD
lines += (prefix + line + System.lineSeparator() + positionMarkerLine)
lastLineNum = pos.line
}
val sb = new StringBuilder
for line <- lines do sb.append(line)
sb.toString
}
/** Used to underline source positions in the stack trace
* pos.source must exist
*/
private def positionMarker(pos: SourcePosition): String =
val trimmed = pos.source.lineContent(pos.start).takeWhile(c => c.isWhitespace).length
val padding = pos.startColumnPadding.substring(trimmed).nn
val carets =
if (pos.startLine == pos.endLine)
"^" * math.max(1, pos.endColumn - pos.startColumn)
else "^"
s"$padding$carets" + System.lineSeparator()
© 2015 - 2025 Weber Informatics LLC | Privacy Policy