1- package dotty .tools .dotc
1+ package dotty .tools
2+ package dotc
23package printing
34
45import scala .language .unsafeNulls
56
7+ import scala .collection .mutable
8+
69import core ._
710import Texts ._ , Types ._ , Flags ._ , Symbols ._ , Contexts ._
8- import collection .mutable
911import Decorators ._
10- import scala .util .control .NonFatal
1112import reporting .Message
1213import util .DiffUtil
1314import Highlighting ._
1415
1516object Formatting {
1617
18+ object ShownDef :
19+ /** Represents a value that has been "shown" and can be consumed by StringFormatter.
20+ * Not just a string because it may be a Seq that StringFormatter will intersperse with the trailing separator.
21+ * Also, it's not a `String | Seq[String]` because then we'd need a Context to call `Showable#show`. We could
22+ * make Context a requirement for a Show instance but then we'd have lots of instances instead of just one ShowAny
23+ * instance. We could also try to make `Show#show` require the Context, but then that breaks the Conversion. */
24+ opaque type Shown = Any
25+ object Shown :
26+ given [A : Show ]: Conversion [A , Shown ] = Show [A ].show(_)
27+
28+ sealed abstract class Show [- T ]:
29+ /** Show a value T by returning a "shown" result. */
30+ def show (x : T ): Shown
31+
32+ /** The base implementation, passing the argument to StringFormatter which will try to `.show` it. */
33+ object ShowAny extends Show [Any ]:
34+ def show (x : Any ): Shown = x
35+
36+ class ShowImplicits2 :
37+ given Show [Product ] = ShowAny
38+
39+ class ShowImplicits1 extends ShowImplicits2 :
40+ given Show [ImplicitRef ] = ShowAny
41+ given Show [Names .Designator ] = ShowAny
42+ given Show [util.SrcPos ] = ShowAny
43+
44+ object Show extends ShowImplicits1 :
45+ inline def apply [A ](using inline z : Show [A ]): Show [A ] = z
46+
47+ given [X : Show ]: Show [Seq [X ]] with
48+ def show (x : Seq [X ]) = x.map(Show [X ].show)
49+
50+ given [A : Show , B : Show ]: Show [(A , B )] with
51+ def show (x : (A , B )) = (Show [A ].show(x._1), Show [B ].show(x._2))
52+
53+ given [X : Show ]: Show [X | Null ] with
54+ def show (x : X | Null ) = if x == null then " null" else Show [X ].show(x.nn)
55+
56+ given Show [FlagSet ] with
57+ def show (x : FlagSet ) = x.flagsString
58+
59+ given Show [TypeComparer .ApproxState ] with
60+ def show (x : TypeComparer .ApproxState ) = TypeComparer .ApproxState .Repr .show(x)
61+
62+ given Show [Showable ] = ShowAny
63+ given Show [Shown ] = ShowAny
64+ given Show [Int ] = ShowAny
65+ given Show [Char ] = ShowAny
66+ given Show [Boolean ] = ShowAny
67+ given Show [String ] = ShowAny
68+ given Show [Class [? ]] = ShowAny
69+ given Show [Exception ] = ShowAny
70+ given Show [StringBuffer ] = ShowAny
71+ given Show [CompilationUnit ] = ShowAny
72+ given Show [Phases .Phase ] = ShowAny
73+ given Show [TyperState ] = ShowAny
74+ given Show [config.ScalaVersion ] = ShowAny
75+ given Show [io.AbstractFile ] = ShowAny
76+ given Show [parsing.Scanners .Scanner ] = ShowAny
77+ given Show [util.SourceFile ] = ShowAny
78+ given Show [util.Spans .Span ] = ShowAny
79+ given Show [tasty.TreeUnpickler # OwnerTree ] = ShowAny
80+ end Show
81+ end ShownDef
82+ export ShownDef .{ Show , Shown }
83+
1784 /** General purpose string formatter, with the following features:
1885 *
19- * 1) On all Showables, `show` is called instead of `toString`
20- * 2) Exceptions raised by a `show` are handled by falling back to `toString`.
21- * 3) Sequences can be formatted using the desired separator between two `%` signs,
86+ * 1. Invokes the `show` extension method on the interpolated arguments.
87+ * 2. Sequences can be formatted using the desired separator between two `%` signs,
2288 * eg `i"myList = (${myList}%, %)"`
23- * 4) Safe handling of multi-line margins. Left margins are skipped om the parts
89+ * 3. Safe handling of multi-line margins. Left margins are stripped on the parts
2490 * of the string context *before* inserting the arguments. That way, we guard
2591 * against accidentally treating an interpolated value as a margin.
2692 */
2793 class StringFormatter (protected val sc : StringContext ) {
28- protected def showArg (arg : Any )(using Context ): String = arg match {
29- case arg : Showable =>
30- try arg.show
31- catch {
32- case ex : CyclicReference => " ... (caught cyclic reference) ..."
33- case NonFatal (ex)
34- if ! ctx.mode.is(Mode .PrintShowExceptions ) &&
35- ! ctx.settings.YshowPrintErrors .value =>
36- val msg = ex match
37- case te : TypeError => te.toMessage
38- case _ => ex.getMessage
39- s " [cannot display due to $msg, raw string = ${arg.toString}] "
40- }
41- case _ => String .valueOf(arg)
42- }
94+ protected def showArg (arg : Any )(using Context ): String = arg.tryToShow
4395
44- private def treatArg (arg : Any , suffix : String )(using Context ): (Any , String ) = arg match {
96+ private def treatArg (arg : Shown , suffix : String )(using Context ): (Any , String ) = arg match {
4597 case arg : Seq [? ] if suffix.nonEmpty && suffix.head == '%' =>
4698 val (rawsep, rest) = suffix.tail.span(_ != '%' )
4799 val sep = StringContext .processEscapes(rawsep)
@@ -51,7 +103,7 @@ object Formatting {
51103 (showArg(arg), suffix)
52104 }
53105
54- def assemble (args : Seq [Any ])(using Context ): String = {
106+ def assemble (args : Seq [Shown ])(using Context ): String = {
55107 def isLineBreak (c : Char ) = c == '\n ' || c == '\f ' // compatible with StringLike#isLineBreak
56108 def stripTrailingPart (s : String ) = {
57109 val (pre, post) = s.span(c => ! isLineBreak(c))
@@ -77,18 +129,6 @@ object Formatting {
77129 override protected def showArg (arg : Any )(using Context ): String =
78130 wrapNonSensical(arg, super .showArg(arg)(using errorMessageCtx))
79131
80- class SyntaxFormatter (sc : StringContext ) extends StringFormatter (sc) {
81- override protected def showArg (arg : Any )(using Context ): String =
82- arg match {
83- case hl : Highlight =>
84- hl.show
85- case hb : HighlightBuffer =>
86- hb.toString
87- case _ =>
88- SyntaxHighlighting .highlight(super .showArg(arg))
89- }
90- }
91-
92132 private def wrapNonSensical (arg : Any , str : String )(using Context ): String = {
93133 import Message ._
94134 def isSensical (arg : Any ): Boolean = arg match {
0 commit comments