Skip to content

Commit d876acf

Browse files
authored
Fix Text wrapping (#16277)
2 parents 1276ba5 + 9852984 commit d876acf

29 files changed

+255
-154
lines changed

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
111111
protected def refinementNameString(tp: RefinedType): String = nameString(tp.refinedName)
112112

113113
/** String representation of a refinement */
114-
protected def toTextRefinement(rt: RefinedType): Closed =
114+
protected def toTextRefinement(rt: RefinedType): Text =
115115
(refinementNameString(rt) ~ toTextRHS(rt.refinedInfo)).close
116116

117117
protected def argText(arg: Type): Text = homogenizeArg(arg) match {

compiler/src/dotty/tools/dotc/printing/Texts.scala

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@ object Texts {
1515
case Vertical(relems) => relems.isEmpty
1616
}
1717

18+
// Str Ver Clo Flu
19+
// isVertical F T F F
20+
// isClosed F T T F
21+
// isFluid F F T T
22+
// isSplittable F F F T
1823
def isVertical: Boolean = isInstanceOf[Vertical]
1924
def isClosed: Boolean = isVertical || isInstanceOf[Closed]
2025
def isFluid: Boolean = isInstanceOf[Fluid]
2126
def isSplittable: Boolean = isFluid && !isClosed
2227

23-
def close: Closed = new Closed(relems)
28+
def close: Text = if isSplittable then Closed(relems) else this
2429

2530
def remaining(width: Int): Int = this match {
2631
case Str(s, _) =>
@@ -53,7 +58,7 @@ object Texts {
5358
}
5459

5560
private def appendIndented(that: Text)(width: Int): Text =
56-
Vertical(that.layout(width - indentMargin).indented :: this.relems)
61+
Fluid(that.layout(width - indentMargin).indented :: this.relems)
5762

5863
private def append(width: Int)(that: Text): Text =
5964
if (this.isEmpty) that.layout(width)
@@ -113,7 +118,7 @@ object Texts {
113118
sb.append("|")
114119
}
115120
}
116-
sb.append(s)
121+
sb.append(s.replaceAll("[ ]+$", ""))
117122
case _ =>
118123
var follow = false
119124
for (elem <- relems.reverse) {
@@ -138,7 +143,13 @@ object Texts {
138143
def ~ (that: Text): Text =
139144
if (this.isEmpty) that
140145
else if (that.isEmpty) this
141-
else Fluid(that :: this :: Nil)
146+
else this match
147+
case Fluid(relems1) if !isClosed => that match
148+
case Fluid(relems2) if !that.isClosed => Fluid(relems2 ++ relems1)
149+
case _ => Fluid(that +: relems1)
150+
case _ => that match
151+
case Fluid(relems2) if !that.isClosed => Fluid(relems2 :+ this)
152+
case _ => Fluid(that :: this :: Nil)
142153

143154
def ~~ (that: Text): Text =
144155
if (this.isEmpty) that
@@ -161,9 +172,9 @@ object Texts {
161172
def apply(xs: Traversable[Text], sep: String = " "): Text =
162173
if (sep == "\n") lines(xs)
163174
else {
164-
val ys = xs filterNot (_.isEmpty)
175+
val ys = xs.filterNot(_.isEmpty)
165176
if (ys.isEmpty) Str("")
166-
else ys reduce (_ ~ sep ~ _)
177+
else ys.reduceRight((a, b) => (a ~ sep).close ~ b)
167178
}
168179

169180
/** The given texts `xs`, each on a separate line */
@@ -176,12 +187,16 @@ object Texts {
176187

177188
case class Str(s: String, lineRange: LineRange = EmptyLineRange) extends Text {
178189
override def relems: List[Text] = List(this)
190+
override def toString = this match
191+
case Str(s, EmptyLineRange) => s"Str($s)"
192+
case Str(s, lineRange) => s"Str($s, $lineRange)"
179193
}
180194

181195
case class Vertical(relems: List[Text]) extends Text
182196
case class Fluid(relems: List[Text]) extends Text
183197

184-
class Closed(relems: List[Text]) extends Fluid(relems)
198+
class Closed(relems: List[Text]) extends Fluid(relems):
199+
override def productPrefix = "Closed"
185200

186201
implicit def stringToText(s: String): Text = Str(s)
187202

compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ object ErrorReporting {
187187
|The tests were made under $constraintText"""
188188

189189
def whyFailedStr(fail: FailedExtension) =
190-
i""" failed with
190+
i"""
191+
|
192+
| failed with:
191193
|
192194
|${fail.whyFailed.message.indented(8)}"""
193195

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,9 +568,9 @@ object Implicits:
568568
if reasons.length > 1 then
569569
reasons.mkString("\n\t* ", "\n\t* ", "")
570570
else
571-
reasons.mkString
571+
reasons.mkString(" ", "", "")
572572

573-
def explanation(using Context) = em"Failed to synthesize an instance of type ${clarify(expectedType)}: ${formatReasons}"
573+
def explanation(using Context) = em"Failed to synthesize an instance of type ${clarify(expectedType)}:${formatReasons}"
574574

575575
end Implicits
576576

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package dotty.tools
2+
package dotc
3+
4+
import core.*, Decorators.*, Symbols.*
5+
import printing.Texts.*
6+
7+
import org.junit.Test
8+
9+
class TupleShowTests extends DottyTest:
10+
def IntType = defn.IntType
11+
def LongType = defn.LongType
12+
def ShortType = defn.ShortType
13+
def Types_10 = List.fill(5)(IntType) ::: List.fill(5)(LongType)
14+
def Types_20 = Types_10 ::: Types_10
15+
16+
val tup0 = defn.tupleType(Nil)
17+
val tup1 = defn.tupleType(IntType :: Nil)
18+
val tup2 = defn.tupleType(IntType :: LongType :: Nil)
19+
val tup3 = defn.tupleType(IntType :: LongType :: ShortType :: Nil)
20+
val tup21 = defn.tupleType(Types_20 ::: IntType :: Nil)
21+
val tup22 = defn.tupleType(Types_20 ::: IntType :: LongType :: Nil)
22+
val tup23 = defn.tupleType(Types_20 ::: IntType :: LongType :: ShortType :: Nil)
23+
val tup24 = defn.tupleType(Types_20 ::: IntType :: LongType :: ShortType :: ShortType :: Nil)
24+
25+
@Test def tup0_show = chkEq("EmptyTuple.type", i"$tup0")
26+
@Test def tup1_show = chkEq("Tuple1[Int]", i"$tup1")
27+
@Test def tup2_show = chkEq("(Int, Long)", i"$tup2")
28+
@Test def tup3_show = chkEq("(Int, Long, Short)", i"$tup3")
29+
@Test def tup21_show = chkEq(res21, i"$tup21")
30+
@Test def tup22_show = chkEq(res22, i"$tup22")
31+
@Test def tup23_show = chkEq(res23, i"$tup23")
32+
@Test def tup24_show = chkEq(res24, i"$tup24")
33+
34+
@Test def tup3_text =
35+
val obt = tup3.toText(ctx.printer)
36+
val exp = Fluid(List(
37+
Str(")"),
38+
Str("Short"),
39+
Closed(List(Str(", "), Str("Long"))),
40+
Closed(List(Str(", "), Str("Int"))),
41+
Str("("),
42+
))
43+
chkEq(exp, obt)
44+
45+
@Test def tup3_layout10 =
46+
val obt = tup3.toText(ctx.printer).layout(10)
47+
val exp = Fluid(List(
48+
Str(" Short)"),
49+
Str(" Long, "),
50+
Str("(Int, "),
51+
))
52+
chkEq(exp, obt)
53+
54+
@Test def tup3_show10 = chkEq("(Int,\n Long,\n Short)", tup3.toText(ctx.printer).mkString(10, false))
55+
56+
val res21 = """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int,
57+
| Int, Long, Long, Long, Long, Long, Int)""".stripMargin
58+
59+
val res22 = """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int,
60+
| Int, Long, Long, Long, Long, Long, Int, Long)""".stripMargin
61+
62+
val res23 = """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int,
63+
| Int, Long, Long, Long, Long, Long, Int, Long, Short)""".stripMargin
64+
65+
val res24 = """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int,
66+
| Int, Long, Long, Long, Long, Long, Int, Long, Short, Short)""".stripMargin
67+
68+
def chkEq[A](expected: A, obtained: A) = assert(expected == obtained, diff(s"$expected", s"$obtained"))
69+
70+
def diff(exp: String, obt: String) =
71+
val min = math.min(exp.length, obt.length)
72+
val pre =
73+
var i = 0
74+
while i < min && exp(i) == obt(i) do i += 1
75+
exp.take(i)
76+
val suf =
77+
val max = min - pre.length - 1
78+
var i = 0
79+
while i <= max && exp(exp.length - 1 - i) == obt(obt.length - 1 - i) do i += 1
80+
exp.drop(exp.length - 1)
81+
82+
import scala.io.AnsiColor.*
83+
val ellip = BLACK + BOLD + "..." + RESET
84+
val compactPre = if pre.length <= 20 then pre else ellip + pre.drop(pre.length - 20)
85+
val compactSuf = if suf.length <= 20 then suf else suf.take(20) + ellip
86+
def extractDiff(s: String) = s.slice(pre.length, s.length - suf.length)
87+
s"""|Comparison Failure:
88+
| expected: $compactPre${CYAN }${extractDiff(exp)}$RESET$compactSuf
89+
| obtained: $compactPre$MAGENTA${extractDiff(obt)}$RESET$compactSuf
90+
|""".stripMargin

compiler/test/dotty/tools/dotc/printing/PrintingTest.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import scala.io.Source
2121
import org.junit.Test
2222
import scala.util.Using
2323
import java.io.File
24+
2425
class PrintingTest {
2526

2627
def options(phase: String, flags: List[String]) =
@@ -45,7 +46,7 @@ class PrintingTest {
4546
}
4647

4748
val actualLines = byteStream.toString(StandardCharsets.UTF_8.name).linesIterator
48-
FileDiff.checkAndDump(path.toString, actualLines.toIndexedSeq, checkFilePath)
49+
FileDiff.checkAndDumpOrUpdate(path.toString, actualLines.toIndexedSeq, checkFilePath)
4950
}
5051

5152
def testIn(testsDir: String, phase: String) =

compiler/test/dotty/tools/vulpix/FileDiff.scala

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,6 @@ object FileDiff {
5050
outFile.writeAll(content.mkString("", EOL, EOL))
5151
}
5252

53-
def checkAndDump(sourceTitle: String, actualLines: Seq[String], checkFilePath: String): Boolean = {
54-
val outFilePath = checkFilePath + ".out"
55-
FileDiff.check(sourceTitle, actualLines, checkFilePath) match {
56-
case Some(msg) =>
57-
FileDiff.dump(outFilePath, actualLines)
58-
println(msg)
59-
println(FileDiff.diffMessage(checkFilePath, outFilePath))
60-
false
61-
case _ =>
62-
val jOutFilePath = Paths.get(outFilePath)
63-
Files.deleteIfExists(jOutFilePath)
64-
true
65-
}
66-
}
67-
6853
def checkAndDumpOrUpdate(sourceTitle: String, actualLines: Seq[String], checkFilePath: String): Boolean = {
6954
val outFilePath = checkFilePath + ".out"
7055
FileDiff.check(sourceTitle, actualLines, checkFilePath) match {

tests/neg/enum-values.check

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
| meaning a values array is not defined.
77
| An extension method was tried, but could not be fully constructed:
88
|
9-
| example.Extensions.values(Tag) failed with
9+
| example.Extensions.values(Tag)
10+
|
11+
| failed with:
1012
|
1113
| Found: example.Tag.type
1214
| Required: Nothing
@@ -18,7 +20,9 @@
1820
| meaning a values array is not defined.
1921
| An extension method was tried, but could not be fully constructed:
2022
|
21-
| example.Extensions.values(ListLike) failed with
23+
| example.Extensions.values(ListLike)
24+
|
25+
| failed with:
2226
|
2327
| Found: Array[example.Tag[?]]
2428
| Required: Array[example.ListLike[?]]
@@ -30,7 +34,9 @@
3034
| meaning a values array is not defined.
3135
| An extension method was tried, but could not be fully constructed:
3236
|
33-
| example.Extensions.values(TypeCtorsK) failed with
37+
| example.Extensions.values(TypeCtorsK)
38+
|
39+
| failed with:
3440
|
3541
| Found: Array[example.Tag[?]]
3642
| Required: Array[example.TypeCtorsK[?[_$1]]]
@@ -63,7 +69,9 @@
6369
| value values is not a member of object example.NotAnEnum.
6470
| An extension method was tried, but could not be fully constructed:
6571
|
66-
| example.Extensions.values(NotAnEnum) failed with
72+
| example.Extensions.values(NotAnEnum)
73+
|
74+
| failed with:
6775
|
6876
| Found: example.NotAnEnum.type
6977
| Required: Nothing

tests/neg/i10901.check

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
| value º is not a member of object BugExp4Point2D.IntT.
55
| An extension method was tried, but could not be fully constructed:
66
|
7-
| º(x) failed with
7+
| º(x)
8+
|
9+
| failed with:
810
|
911
| Ambiguous overload. The overloaded alternatives of method º in object dsl with types
1012
| [T1, T2]
@@ -22,7 +24,9 @@
2224
|value º is not a member of object BugExp4Point2D.IntT.
2325
|An extension method was tried, but could not be fully constructed:
2426
|
25-
| º(x) failed with
27+
| º(x)
28+
|
29+
| failed with:
2630
|
2731
| Ambiguous overload. The overloaded alternatives of method º in object dsl with types
2832
| [T1, T2]
@@ -36,6 +40,8 @@
3640
| value foo is not a member of String.
3741
| An extension method was tried, but could not be fully constructed:
3842
|
39-
| Test.foo("abc")(/* missing */summon[C]) failed with
43+
| Test.foo("abc")(/* missing */summon[C])
44+
|
45+
| failed with:
4046
|
4147
| No given instance of type C was found for parameter x$2 of method foo in object Test

tests/neg/i13558.check

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
| value id is not a member of testcode.A.
55
| An extension method was tried, but could not be fully constructed:
66
|
7-
| testcode.ExtensionA.id(a) failed with
7+
| testcode.ExtensionA.id(a)
8+
|
9+
| failed with:
810
|
911
| Reference to id is ambiguous,
1012
| it is both imported by import testcode.ExtensionB._
@@ -15,7 +17,9 @@
1517
| value id is not a member of testcode.A.
1618
| An extension method was tried, but could not be fully constructed:
1719
|
18-
| testcode.ExtensionB.id(a) failed with
20+
| testcode.ExtensionB.id(a)
21+
|
22+
| failed with:
1923
|
2024
| Reference to id is ambiguous,
2125
| it is both imported by import testcode.ExtensionA._

tests/neg/i14127.check

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
-- Error: tests/neg/i14127.scala:6:55 ----------------------------------------------------------------------------------
22
6 | *: Int *: Int *: Int *: Int *: Int *: EmptyTuple)]] // error
33
| ^
4-
|No given instance of type deriving.Mirror.Of[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int,
5-
| Int
6-
|, Int, Int)] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int,
7-
| Int
8-
|, Int, Int)]:
4+
|No given instance of type deriving.Mirror.Of[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int,
5+
| Int, Int, Int)] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int,
6+
| Int, Int, Int)]:
97
| * class *: is not a generic product because it reduces to a tuple with arity 23, expected arity <= 22
108
| * class *: is not a generic sum because it does not have subclasses

tests/neg/i14432.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-- Error: tests/neg/i14432.scala:13:33 ---------------------------------------------------------------------------------
22
13 |val mFoo = summon[Mirror.Of[Foo]] // error: no mirror found
33
| ^
4-
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
4+
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
55
| * class Foo is not a generic product because the constructor of class Foo is innaccessible from the calling scope.
66
| * class Foo is not a generic sum because it is not a sealed class

tests/neg/i14432a.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-- Error: tests/neg/i14432a.scala:14:43 --------------------------------------------------------------------------------
22
14 | val mFoo = summon[Mirror.Of[example.Foo]] // error: no mirror found
33
| ^
4-
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
4+
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
55
| * class Foo is not a generic product because the constructor of class Foo is innaccessible from the calling scope.
66
| * class Foo is not a generic sum because it is not a sealed class

tests/neg/i14432b.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-- Error: tests/neg/i14432b.scala:15:43 --------------------------------------------------------------------------------
22
15 | val mFoo = summon[Mirror.Of[example.Foo]] // error: no mirror found
33
| ^
4-
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
4+
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
55
| * class Foo is not a generic product because the constructor of class Foo is innaccessible from the calling scope.
66
| * class Foo is not a generic sum because it is not a sealed class

tests/neg/i14432c.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
-- Error: tests/neg/i14432c.scala:16:43 --------------------------------------------------------------------------------
66
16 | val mFoo = summon[Mirror.Of[example.Foo]] // error: no mirror
77
| ^
8-
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
8+
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
99
| * class Foo is not a generic product because the constructor of class Foo is innaccessible from the calling scope.
1010
| * class Foo is not a generic sum because it is not a sealed class

tests/neg/i14432d.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-- Error: tests/neg/i14432d.scala:17:45 --------------------------------------------------------------------------------
22
17 | val mFoo = summon[Mirror.Of[example.Foo]] // error
33
| ^
4-
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
4+
|No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]:
55
| * class Foo is not a generic product because the constructor of class Foo is innaccessible from the calling scope.
66
| * class Foo is not a generic sum because it is not a sealed class

0 commit comments

Comments
 (0)