All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dotty.tools.dotc.reporting.trace.scala Maven / Gradle / Ivy

There is a newer version: 3.6.4-RC1-bin-20241220-0bfa1af-NIGHTLY
Show newest version
package dotty.tools
package dotc
package reporting

import scala.language.unsafeNulls

import core.*, Contexts.*, Decorators.*
import config.*
import printing.Formatting.*

import scala.compiletime.*

/** Exposes the {{{ trace("question") { op } }}} syntax.
 *
 * Traced operations will print indented messages if enabled.
 * Tracing depends on [[Config.tracingEnabled]] and [[dotty.tools.dotc.config.ScalaSettings.Ylog]].
 * Tracing can be forced by replacing [[trace]] with [[trace.force]] or [[trace.log]] (see below).
 */
object trace extends TraceSyntax:
  inline def isEnabled = Config.tracingEnabled
  protected val isForced = false

  object force extends TraceSyntax:
    inline def isEnabled: true = true
    protected val isForced = true

  object log extends TraceSyntax:
    inline def isEnabled: true = true
    protected val isForced = false

  def dumpStack(limit: Int = -1): Unit = {
    val out = Console.out
    val exc = new Exception("Dump Stack")
    var stack = exc.getStackTrace
      .filter(e => !e.getClassName.startsWith("dotty.tools.dotc.reporting.TraceSyntax"))
      .filter(e => !e.getClassName.startsWith("dotty.tools.dotc.reporting.trace"))
    if limit >= 0 then
      stack = stack.take(limit)
    exc.setStackTrace(stack)
    exc.printStackTrace(out)
  }
end trace

/** This module is carefully optimized to give zero overhead if Config.tracingEnabled
 *  is false. The `trace` operation is called in various hotspots, so every tiny bit
 *  of overhead is unacceptable: boxing, closures, additional method calls are all out.
 */
trait TraceSyntax:

  inline def isEnabled: Boolean
  protected val isForced: Boolean

  inline def onDebug[TD](inline question: String)(inline op: TD)(using Context): TD =
    conditionally(ctx.settings.YdebugTrace.value, question, false)(op)

  inline def conditionally[TC](inline cond: Boolean, inline question: String, inline show: Boolean)(inline op: TC)(using Context): TC =
    inline if isEnabled then
      apply(question, if cond then Printers.default else Printers.noPrinter, show)(op)
    else op

  inline def apply[T, U >: T](inline question: String, inline printer: Printers.Printer, inline showOp: U => String)(inline op: T)(using Context): T =
    inline if isEnabled then
      doTrace[T](question, printer, showOp)(op)
    else op

  inline def apply[T](inline question: String, inline printer: Printers.Printer, inline show: Boolean)(inline op: T)(using Context): T =
    apply(question, printer, {
      val showOp: T => String = inline if show == true then
        val showT = summonInline[Show[T]]
        {
          given Show[T] = showT
          t => i"$t"
        }
      else
        summonFrom {
          case given Show[T] => t => i"$t"
          case _             => alwaysToString
        }
      showOp
    })(op)

  inline def apply[T](inline question: String, inline printer: Printers.Printer)(inline op: T)(using Context): T =
    apply[T](question, printer, false)(op)

  inline def apply[T](inline question: String, inline show: Boolean)(inline op: T)(using Context): T =
    apply[T](question, Printers.default, show)(op)

  inline def apply[T](inline question: String)(inline op: T)(using Context): T =
    apply[T](question, false)(op)

  private val alwaysToString = (x: Any) => String.valueOf(x)

  private def doTrace[T](question: => String,
                         printer: Printers.Printer = Printers.default,
                         showOp: T => String)
                        (op: => T)(using Context): T =
    if ctx.mode.is(Mode.Printing) || !isForced && (printer eq Printers.noPrinter) then op
    else
      val start = System.nanoTime
      // Avoid evaluating question multiple time, since each evaluation
      // may cause some extra logging output.
      val q = question
      val leading = s"==> $q?"
      val trailing = (res: T) => s"<== $q = ${showOp(res)}"
      var finalized = false
      var logctx = ctx
      while logctx.reporter.isInstanceOf[StoreReporter] do logctx = logctx.outer
      def margin = ctx.base.indentTab * ctx.base.indent
      def doLog(s: String) = if isForced then println(s) else report.log(s)(using logctx)
      def finalize(msg: String) =
        if !finalized then
          ctx.base.indent -= 1
          val stop = System.nanoTime
          val diffNs = stop - start
          val diffS = (diffNs / 1000 / 1000).toInt / 1000.0
          if diffS > 0.1 then
            doLog(s"$margin$msg (${"%.2f".format(diffS)} s)")
          else
            doLog(s"$margin$msg")
          finalized = true
      try
        doLog(s"$margin$leading")
        ctx.base.indent += 1
        val res = op
        finalize(trailing(res))
        res
      catch
        case ex: runtime.NonLocalReturnControl[T @unchecked] =>
          finalize(trailing(ex.value))
          throw ex
        case ex: Throwable =>
          val msg = s"<== $q =  (with exception $ex)"
          finalize(msg)
          throw ex
end TraceSyntax




© 2015 - 2025 Weber Informatics LLC | Privacy Policy