Skip to content

Commit 8d4d9a3

Browse files
committed
New string infterpolators
Roll `sm` and `i` into one interpolator (also called `i`) Evolve `d` to `em` interpolator (for error messages) New interpolator `ex` with more explanations, replaces disambiguation.
1 parent d5f4268 commit 8d4d9a3

31 files changed

+420
-349
lines changed

src/dotty/tools/dotc/ast/NavigateAST.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ object NavigateAST {
2222
Error(i"""no untyped tree for $tree, pos = ${tree.pos}, envelope = ${tree.envelope}
2323
|best matching path =\n$loosePath%\n====\n%
2424
|path positions = ${loosePath.map(_.pos)}
25-
|path envelopes = ${loosePath.map(_.envelope)}""".stripMargin)
25+
|path envelopes = ${loosePath.map(_.envelope)}""")
2626
}
2727

2828
/** The reverse path of untyped trees starting with a tree that closest matches

src/dotty/tools/dotc/config/CompilerCommand.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object CompilerCommand extends DotClass {
1313
/** The name of the command */
1414
def cmdName = "scalac"
1515

16-
private def explainAdvanced = "\n" + """
16+
private def explainAdvanced = """
1717
|-- Notes on option parsing --
1818
|Boolean settings are always false unless set.
1919
|Where multiple values are accepted, they should be comma-separated.
@@ -26,7 +26,7 @@ object CompilerCommand extends DotClass {
2626
| example: -Ylog:erasure+ logs the erasure phase and the phase after the erasure phase.
2727
| This is useful because during the tree transform of phase X, we often
2828
| already are in phase X + 1.
29-
""".stripMargin.trim + "\n"
29+
"""
3030

3131
def shortUsage = s"Usage: $cmdName <options> <source files>"
3232

src/dotty/tools/dotc/core/Constraint.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ abstract class Constraint extends Showable {
3232
def contains(tvar: TypeVar): Boolean
3333

3434
/** The constraint entry for given type parameter `param`, or NoType if `param` is not part of
35-
* the constraint domain.
35+
* the constraint domain. Note: Low level, implementation dependent.
3636
*/
3737
def entry(param: PolyParam): Type
3838

src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 13 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import util.Positions.Position, util.SourcePosition
88
import collection.mutable.ListBuffer
99
import dotty.tools.dotc.transform.TreeTransforms._
1010
import scala.language.implicitConversions
11+
import printing.Formatting._
1112

1213
/** This object provides useful implicit decorators for types defined elsewhere */
1314
object Decorators {
@@ -150,72 +151,23 @@ object Decorators {
150151
implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition =
151152
ctx.source.atPos(pos)
152153

153-
/** The i"..." string interpolator adds two features to the s interpolator:
154-
* 1) On all Showables, `show` is called instead of `toString`
155-
* 2) Lists can be formatted using the desired separator between two `%` signs,
156-
* eg `i"myList = (${myList}%, %)"`
157-
*/
158154
implicit class StringInterpolators(val sc: StringContext) extends AnyVal {
159155

160-
def i(args: Any*)(implicit ctx: Context): String = {
161-
162-
def treatArg(arg: Any, suffix: String): (Any, String) = arg match {
163-
case arg: Seq[_] if suffix.nonEmpty && suffix.head == '%' =>
164-
val (rawsep, rest) = suffix.tail.span(_ != '%')
165-
val sep = StringContext.treatEscapes(rawsep)
166-
if (rest.nonEmpty) (arg map treatSingleArg mkString sep, rest.tail)
167-
else (arg, suffix)
168-
case _ =>
169-
(treatSingleArg(arg), suffix)
170-
}
171-
172-
def treatSingleArg(arg: Any) : Any =
173-
try
174-
arg match {
175-
case arg: Showable => arg.show(ctx.addMode(Mode.FutureDefsOK))
176-
case _ => arg
177-
}
178-
catch {
179-
case ex: Exception => throw ex // s"(missing due to $ex)"
180-
}
156+
/** General purpose string formatting */
157+
def i(args: Any*)(implicit ctx: Context): String =
158+
new StringFormatter(sc).assemble(args)
181159

182-
val prefix :: suffixes = sc.parts.toList
183-
val (args1, suffixes1) = (args, suffixes).zipped.map(treatArg(_, _)).unzip
184-
new StringContext(prefix :: suffixes1.toList: _*).s(args1: _*)
185-
}
160+
/** Formatting for error messages: Like `i` but suppress follow-on
161+
* error messages after the first one if some of their arguments are "non-sensical".
162+
*/
163+
def em(args: Any*)(implicit ctx: Context): String =
164+
new ErrorMessageFormatter(sc).assemble(args)
186165

187-
/** Lifted from scala.reflect.internal.util
188-
* A safe combination of [[scala.collection.immutable.StringLike#stripMargin]]
189-
* and [[scala.StringContext#raw]].
190-
*
191-
* The margin of each line is defined by whitespace leading up to a '|' character.
192-
* This margin is stripped '''before''' the arguments are interpolated into to string.
193-
*
194-
* String escape sequences are '''not''' processed; this interpolater is designed to
195-
* be used with triple quoted Strings.
196-
*
197-
* {{{
198-
* scala> val foo = "f|o|o"
199-
* foo: String = f|o|o
200-
* scala> sm"""|${foo}
201-
* |"""
202-
* res0: String =
203-
* "f|o|o
204-
* "
205-
* }}}
166+
/** Formatting with added explanations: Like `em`, but add explanations to
167+
* give more info about type variables and to disambiguate where needed.
206168
*/
207-
final def sm(args: Any*): String = {
208-
def isLineBreak(c: Char) = c == '\n' || c == '\f' // compatible with StringLike#isLineBreak
209-
def stripTrailingPart(s: String) = {
210-
val (pre, post) = s.span(c => !isLineBreak(c))
211-
pre + post.stripMargin
212-
}
213-
val stripped: List[String] = sc.parts.toList match {
214-
case head :: tail => head.stripMargin :: (tail map stripTrailingPart)
215-
case Nil => Nil
216-
}
217-
new StringContext(stripped: _*).raw(args: _*)
218-
}
169+
def ex(args: Any*)(implicit ctx: Context): String =
170+
explained2(implicit ctx => em(args: _*))
219171
}
220172
}
221173

src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,7 @@ object Denotations {
975975
| $sym2: ${sym2.info};
976976
|they are both defined in ${sym1.owner} but have matching signatures
977977
| ${denot1.info} and
978-
| ${denot2.info}$fromWhere""".stripMargin,
978+
| ${denot2.info}$fromWhere""",
979979
denot2.info, denot2.info)
980980
}
981981

src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -669,19 +669,19 @@ object SymDenotations {
669669
val cls = owner.enclosingSubClass
670670
if (!cls.exists)
671671
fail(
672-
s""" Access to protected $this not permitted because
672+
i""" Access to protected $this not permitted because
673673
| enclosing ${ctx.owner.enclosingClass.showLocated} is not a subclass of
674-
| ${owner.showLocated} where target is defined""".stripMargin)
674+
| ${owner.showLocated} where target is defined""")
675675
else if (
676676
!( isType // allow accesses to types from arbitrary subclasses fixes #4737
677677
|| pre.baseTypeRef(cls).exists // ??? why not use derivesFrom ???
678678
|| isConstructor
679679
|| (owner is ModuleClass) // don't perform this check for static members
680680
))
681681
fail(
682-
s""" Access to protected ${symbol.show} not permitted because
682+
i""" Access to protected ${symbol.show} not permitted because
683683
| prefix type ${pre.widen.show} does not conform to
684-
| ${cls.showLocated} where the access takes place""".stripMargin)
684+
| ${cls.showLocated} where the access takes place""")
685685
else true
686686
}
687687

@@ -1933,10 +1933,10 @@ object SymDenotations {
19331933
else ("", "the signature")
19341934
val name = ctx.fresh.setSetting(ctx.settings.debugNames, true).nameString(denot.name)
19351935
ctx.error(
1936-
s"""|bad symbolic reference. A signature$location
1937-
|refers to $name in ${denot.owner.showKind} ${denot.owner.showFullName} which is not available.
1938-
|It may be completely missing from the current classpath, or the version on
1939-
|the classpath might be incompatible with the version used when compiling $src.""".stripMargin)
1936+
i"""bad symbolic reference. A signature$location
1937+
|refers to $name in ${denot.owner.showKind} ${denot.owner.showFullName} which is not available.
1938+
|It may be completely missing from the current classpath, or the version on
1939+
|the classpath might be incompatible with the version used when compiling $src.""")
19401940
if (ctx.debug) throw new Error()
19411941
initializeToDefaults(denot)
19421942
}

src/dotty/tools/dotc/core/SymbolLoaders.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ class SymbolLoaders {
7070
// require yjp.jar at runtime. See SI-2089.
7171
if (ctx.settings.termConflict.isDefault)
7272
throw new TypeError(
73-
sm"""$owner contains object and package with same name: $pname
74-
|one of them needs to be removed from classpath""")
73+
i"""$owner contains object and package with same name: $pname
74+
|one of them needs to be removed from classpath""")
7575
else if (ctx.settings.termConflict.value == "package") {
7676
ctx.warning(
7777
s"Resolving package/object name conflict in favor of package ${preExisting.fullName}. The object will be inaccessible.")

src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ object Symbols {
510510
def toText(printer: Printer): Text = printer.toText(this)
511511

512512
def showLocated(implicit ctx: Context): String = ctx.locatedText(this).show
513+
def showExtendedLocation(implicit ctx: Context): String = ctx.extendedLocationText(this).show
513514
def showDcl(implicit ctx: Context): String = ctx.dclText(this).show
514515
def showKind(implicit ctx: Context): String = ctx.kindString(this)
515516
def showName(implicit ctx: Context): String = ctx.nameString(this)

src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import Types._, Contexts._, Symbols._, Flags._, Names._, NameOps._, Denotations.
66
import Decorators._
77
import StdNames.{nme, tpnme}
88
import collection.mutable
9-
import printing.Disambiguation.disambiguated
109
import util.{Stats, DotClass, SimpleMap}
1110
import config.Config
1211
import config.Printers._
@@ -1469,7 +1468,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
14691468

14701469
/** Show subtype goal that led to an assertion failure */
14711470
def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context) = {
1472-
println(disambiguated(implicit ctx => s"assertion failure for ${tp1.show} <:< ${tp2.show}, frozen = $frozenConstraint"))
1471+
println(ex"assertion failure for $tp1 <:< $tp2, frozen = $frozenConstraint")
14731472
def explainPoly(tp: Type) = tp match {
14741473
case tp: PolyParam => ctx.echo(s"polyparam ${tp.show} found in ${tp.binder.show}")
14751474
case tp: TypeRef if tp.symbol.exists => ctx.echo(s"typeref ${tp.show} found in ${tp.symbol.owner.show}")

src/dotty/tools/dotc/core/Types.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,11 +1493,11 @@ object Types {
14931493
(sym.owner.derivesFrom(lastSymbol.owner) ||
14941494
selfTypeOf(sym).derivesFrom(lastSymbol.owner) ||
14951495
selfTypeOf(lastSymbol).derivesFrom(sym.owner))),
1496-
s"""data race? overwriting symbol of type ${this.show},
1497-
|long form = $this of class ${this.getClass},
1496+
i"""data race? overwriting symbol of type $this,
1497+
|long form = $toString of class $getClass,
14981498
|last sym id = ${lastSymbol.id}, new sym id = ${sym.id},
14991499
|last owner = ${lastSymbol.owner}, new owner = ${sym.owner},
1500-
|period = ${ctx.phase} at run ${ctx.runId}""".stripMargin)
1500+
|period = ${ctx.phase} at run ${ctx.runId}""")
15011501
}
15021502

15031503
protected def sig: Signature = Signature.NotAMethod
@@ -3799,7 +3799,7 @@ object Types {
37993799

38003800
class MissingType(pre: Type, name: Name)(implicit ctx: Context) extends TypeError(
38013801
i"""cannot resolve reference to type $pre.$name
3802-
|the classfile defining the type might be missing from the classpath${otherReason(pre)}""".stripMargin) {
3802+
|the classfile defining the type might be missing from the classpath${otherReason(pre)}""") {
38033803
if (ctx.debug) printStackTrace()
38043804
}
38053805

src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,21 @@ class ClassfileParser(
5656
case e: RuntimeException =>
5757
if (ctx.debug) e.printStackTrace()
5858
throw new IOException(
59-
sm"""class file $classfile is broken, reading aborted with ${e.getClass}
60-
|${Option(e.getMessage).getOrElse("")}""")
59+
i"""class file $classfile is broken, reading aborted with ${e.getClass}
60+
|${Option(e.getMessage).getOrElse("")}""")
6161
}
6262

6363
private def parseHeader(): Unit = {
6464
val magic = in.nextInt
6565
if (magic != JAVA_MAGIC)
66-
throw new IOException("class file '" + in.file + "' "
67-
+ "has wrong magic number 0x" + toHexString(magic)
68-
+ ", should be 0x" + toHexString(JAVA_MAGIC))
66+
throw new IOException(s"class file '${in.file}' has wrong magic number 0x${toHexString(magic)}, should be 0x${toHexString(JAVA_MAGIC)}")
6967
val minorVersion = in.nextChar.toInt
7068
val majorVersion = in.nextChar.toInt
7169
if ((majorVersion < JAVA_MAJOR_VERSION) ||
7270
((majorVersion == JAVA_MAJOR_VERSION) &&
7371
(minorVersion < JAVA_MINOR_VERSION)))
74-
throw new IOException("class file '" + in.file + "' "
75-
+ "has unknown version "
76-
+ majorVersion + "." + minorVersion
77-
+ ", should be at least "
78-
+ JAVA_MAJOR_VERSION + "." + JAVA_MINOR_VERSION)
72+
throw new IOException(
73+
s"class file '${in.file}' has unknown version $majorVersion.$minorVersion, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION")
7974
}
8075

8176
/** Return the class symbol of the given name. */
@@ -817,12 +812,12 @@ class ClassfileParser(
817812
getMember(owner, innerName.toTypeName)
818813
}
819814
assert(result ne NoSymbol,
820-
sm"""failure to resolve inner class:
821-
|externalName = $externalName,
822-
|outerName = $outerName,
823-
|innerName = $innerName
824-
|owner.fullName = ${owner.showFullName}
825-
|while parsing ${classfile}""")
815+
i"""failure to resolve inner class:
816+
|externalName = $externalName,
817+
|outerName = $outerName,
818+
|innerName = $innerName
819+
|owner.fullName = ${owner.showFullName}
820+
|while parsing ${classfile}""")
826821
result
827822

828823
case None =>

src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
177177

178178
protected def errorBadSignature(msg: String, original: Option[RuntimeException] = None)(implicit ctx: Context) = {
179179
val ex = new BadSignature(
180-
sm"""error reading Scala signature of $classRoot from $source:
181-
|error occurred at position $readIndex: $msg""")
180+
i"""error reading Scala signature of $classRoot from $source:
181+
|error occurred at position $readIndex: $msg""")
182182
if (ctx.debug || true) original.getOrElse(ex).printStackTrace() // temporarily enable printing of original failure signature to debug failing builds
183183
throw ex
184184
}

src/dotty/tools/dotc/printing/Disambiguation.scala

Lines changed: 0 additions & 86 deletions
This file was deleted.

0 commit comments

Comments
 (0)