diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 5dff8890d336..e4a7efea61b6 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -43,26 +43,33 @@ private[repl] class Rendering(compiler: ReplCompiler, myClassLoader } - /** Load the value of the symbol using reflection + /** Load the value of the symbol using reflection. * * Calling this method evaluates the expression using reflection */ private[this] def valueOf(sym: Symbol)(implicit ctx: Context): Option[String] = { val defn = ctx.definitions - val objectName = sym.owner.fullName.encode.toString.dropRight(1) // gotta drop the '$' + val objectName = sym.owner.fullName.encode.toString.stripSuffix("$") val resObj: Class[_] = Class.forName(objectName, true, classLoader()) - - val res = + val value = resObj - .getDeclaredMethods.find(_.getName == sym.name.toString + "Show").get - .invoke(null).toString - + .getDeclaredMethods.find(_.getName == sym.name.encode.toString) + .map(_.invoke(null)) + val string = value.map { + case null => "null" // Calling .toString on null => NPE + case "" => "\"\"" // Special cased for empty string, following scalac + case a: Array[_] => a.mkString("Array(", ", ", ")") + case x => x.toString + } if (!sym.is(Flags.Method) && sym.info == defn.UnitType) None - else if (res.startsWith(str.REPL_SESSION_LINE)) - Some(res.drop(str.REPL_SESSION_LINE.length).dropWhile(c => c.isDigit || c == '$')) else - Some(res) + string.map { s => + if (s.startsWith(str.REPL_SESSION_LINE)) + s.drop(str.REPL_SESSION_LINE.length).dropWhile(c => c.isDigit || c == '$') + else + s + } } /** Render method definition result */ diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index 67dff5ae2ac6..c6e7e2f49a1c 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -53,7 +53,7 @@ class ReplCompiler(val directory: AbstractFile) extends Compiler { } (1 to objectIndex) - .foldLeft(addImport("dotty.Show".toTermName)(initCtx)) { (ictx, i) => + .foldLeft(initCtx) { (ictx, i) => addImport(nme.EMPTY_PACKAGE ++ "." ++ objectNames(i))(ictx) } } @@ -74,67 +74,22 @@ class ReplCompiler(val directory: AbstractFile) extends Compiler { implicit val ctx: Context = state.run.runContext - def createShow(name: TermName, pos: Position) = { - val showName = name ++ "Show" - val select = Select(Ident(name), "show".toTermName) - val valAsAnyRef = TypeApply(Select(Ident(name), nme.asInstanceOf_), - List(Ident(tpnme.AnyRef))) - val cond = InfixOp(valAsAnyRef, - Ident(nme.EQ), - Literal(Constant(null))) - val showWithNullCheck = If(cond, Literal(Constant("null")), select) - DefDef(showName, Nil, Nil, TypeTree(), showWithNullCheck).withFlags(Synthetic).withPos(pos) - } - - def createPatDefShows(patDef: PatDef) = { - def createDeepShows(tree: untpd.Tree) = { - class PatFolder extends UntypedDeepFolder[List[DefDef]] ( - (acc, tree) => tree match { - case Ident(name) if name.isVariableName && name != nme.WILDCARD => - createShow(name.toTermName, tree.pos) :: acc - case Bind(name, _) if name.isVariableName && name != nme.WILDCARD => - createShow(name.toTermName, tree.pos) :: acc - case _ => - acc - } - ) - (new PatFolder).apply(Nil, tree).reverse - } - - // cannot fold over the whole tree because we need to generate show methods - // for top level identifier starting with an uppercase (e.g. val X, Y = 2) - patDef.pats.flatMap { - case id @ Ident(name) if name != nme.WILDCARD => - List(createShow(name.toTermName, id.pos)) - case bd @ Bind(name, body) if name != nme.WILDCARD => - createShow(name.toTermName, bd.pos) :: createDeepShows(body) - case other => - createDeepShows(other) - } - } - var valIdx = state.valIndex val defs = trees.flatMap { - case vd: ValDef => - List(vd, createShow(vd.name, vd.pos)) - case pd: PatDef => - pd :: createPatDefShows(pd) case expr @ Assign(id: Ident, rhs) => // special case simple reassignment (e.g. x = 3) // in order to print the new value in the REPL val assignName = (id.name ++ str.REPL_ASSIGN_SUFFIX).toTermName val assign = ValDef(assignName, TypeTree(), id).withPos(expr.pos) - val show = createShow(assignName, expr.pos) - List(expr, assign, show) + List(expr, assign) case expr if expr.isTerm => val resName = (str.REPL_RES_PREFIX + valIdx).toTermName valIdx += 1 - val show = createShow(resName, expr.pos) val vd = ValDef(resName, TypeTree(), expr).withPos(expr.pos) - List(vd, show) + vd :: Nil case other => - List(other) + other :: Nil } Definitions( @@ -154,7 +109,6 @@ class ReplCompiler(val directory: AbstractFile) extends Compiler { * package { * object rs$line$nextId { * import rs$line${i <- 0 until nextId}._ - * import dotty.Show._ * * * } diff --git a/compiler/test-resources/repl/i3388 b/compiler/test-resources/repl/i3388 index 8a8f5780f2f4..2477a5bf7c23 100644 --- a/compiler/test-resources/repl/i3388 +++ b/compiler/test-resources/repl/i3388 @@ -1,3 +1,3 @@ scala> val foo = "1"; foo.toInt -val foo: String = "1" +val foo: String = 1 val res0: Int = 1 diff --git a/compiler/test-resources/repl/renderArray b/compiler/test-resources/repl/renderArray new file mode 100644 index 000000000000..cce49a8bb011 --- /dev/null +++ b/compiler/test-resources/repl/renderArray @@ -0,0 +1,2 @@ +scala> val a = Array(1, 2, 3) +val a: Array[Int] = Array(1, 2, 3) diff --git a/compiler/test-resources/repl/renderEmptyString b/compiler/test-resources/repl/renderEmptyString new file mode 100644 index 000000000000..7b1c50f09fad --- /dev/null +++ b/compiler/test-resources/repl/renderEmptyString @@ -0,0 +1,2 @@ +scala> val s = "" +val s: String = "" diff --git a/compiler/test-resources/type-printer/prefixless b/compiler/test-resources/type-printer/prefixless index 2101db7d3dff..dfc014230b17 100644 --- a/compiler/test-resources/type-printer/prefixless +++ b/compiler/test-resources/type-printer/prefixless @@ -1,7 +1,7 @@ scala> List(1,2,3) val res0: List[Int] = List(1, 2, 3) scala> Map("foo" -> 1) -val res1: Map[String, Int] = Map("foo" -> 1) +val res1: Map[String, Int] = Map(foo -> 1) scala> Seq('a','b') val res2: Seq[Char] = List(a, b) scala> Set(4, 5) diff --git a/compiler/test-resources/type-printer/vals b/compiler/test-resources/type-printer/vals index cfffdf183c99..21cd311cf284 100644 --- a/compiler/test-resources/type-printer/vals +++ b/compiler/test-resources/type-printer/vals @@ -7,4 +7,4 @@ val xs: List[Int] = List(1) scala> scala.util.Try(1) val res0: scala.util.Try[Int] = Success(1) scala> Map(1 -> "one") -val res1: Map[Int, String] = Map(1 -> "one") +val res1: Map[Int, String] = Map(1 -> one) diff --git a/library/src/dotty/Show.scala b/library/src/dotty/Show.scala deleted file mode 100644 index 8dcbbc4d76d1..000000000000 --- a/library/src/dotty/Show.scala +++ /dev/null @@ -1,81 +0,0 @@ -package dotty - -trait Show[T] { - def show(t: T): String -} - -trait LowPrioShow { - implicit def defaultShow[T]: Show[T] = new Show[T] { - def show(x: T) = x.toString - } -} - -object Show extends LowPrioShow { - /** This class implements pimping of all types to provide a show method. - * Currently it is quite permissive, if there's no instance of `Show[T]` for - * any `T`, we default to `T#toString`. - */ - implicit class ShowValue[V](val v: V) extends AnyVal { - def show(implicit ev: Show[V]): String = if (v == null) "null" else ev.show(v) - } - - /** Adds escaping backslashes in a string so that it could be put in source code. - * For example, a newline character is shown as '\n' (without the quotes). - * Chars that don't have to be escaped are simply converted to a string. - * @param c the char to be escaped - * @return a string describing how to escape the give char in source code. - */ - private def escapeChar(c: Char): String = c match { - // From 2.12 spec, `charEscapeSeq`: - // ‘\‘ (‘b‘ | ‘t‘ | ‘n‘ | ‘f‘ | ‘r‘ | ‘"‘ | ‘'‘ | ‘\‘) - case '\b' => "\\b" - case '\t' => "\\t" - case '\n' => "\\n" - case '\f' => "\\f" - case '\r' => "\\r" - case '\'' => "\\'" - case '\"' => "\\\"" - case '\\' => "\\\\" - case c => c.toString - } - - implicit val stringShow: Show[String] = new Show[String] { - def show(str: String) = str.flatMap(escapeChar).mkString("\"", "", "\"") - } - - implicit val floatShow: Show[Float] = new Show[Float] { - def show(f: Float) = f + "f" - } - - implicit val charShow: Show[Char] = new Show[Char] { - def show(c: Char) = s"'${escapeChar(c)}'" - } - - implicit def showList[T](implicit st: Show[T]): Show[List[T]] = new Show[List[T]] { - def show(xs: List[T]) = - if (xs.isEmpty) "List()" - else "List(" + xs.map(_.show).mkString(", ") + ")" - } - - implicit def arrayShow[T](implicit st: Show[T]): Show[Array[T]] = new Show[Array[T]] { - def show(xs: Array[T]): String = - if (xs.isEmpty) "Array()" - else "Array(" + xs.map(_.show).mkString(", ") + ")" - } - - implicit def showOption[T](implicit st: Show[T]): Show[Option[T]] = new Show[Option[T]] { - def show(ot: Option[T]): String = ot match { - case Some(t) => "Some("+ st.show(t) + ")" - case none => "None" - } - } - - implicit def showSome[T](implicit st: Show[T]): Show[Some[T]] = new Show[Some[T]] { - def show(ot: Some[T]): String = "Some("+ st.show(ot.get) + ")" - } - - implicit def showMap[K,V](implicit sk: Show[K], sv: Show[V]): Show[Map[K,V]] = new Show[Map[K,V]] { - def show(m: Map[K, V]) = - "Map(" + m.map { case (k, v) => sk.show(k) + " -> " + sv.show(v) } .mkString (", ") + ")" - } -} diff --git a/library/test/dotty/ShowTests.scala b/library/test/dotty/ShowTests.scala deleted file mode 100644 index 57096d1e6d1d..000000000000 --- a/library/test/dotty/ShowTests.scala +++ /dev/null @@ -1,94 +0,0 @@ -package dotty - -import org.junit.Test -import org.junit.Assert._ - -class ShowTests { - import Show._ - - @Test def showString = { - assertEquals(""""\\"""", "\\".show) - assertEquals("\"\\thello world!\"", "\thello world!".show) - assertEquals("\"\\nhello world!\"", "\nhello world!".show) - assertEquals("\"\\rhello world!\"", "\rhello world!".show) - assertEquals(""""\b\t\n\f\r\'\"\\"""", "\b\t\n\f\r\'\"\\".show) - } - - @Test def showFloat = { - assertEquals("1.0f", 1.0f.show) - assertEquals("1.0f", 1.0F.show) - } - - @Test def showDouble = { - assertEquals("1.0", 1.0d.show) - assertEquals("1.0", 1.0.show) - } - - @Test def showChar = { - assertEquals("'\\b'", '\b'.show) - assertEquals("'\\t'", '\t'.show) - assertEquals("'\\n'", '\n'.show) - assertEquals("'\\f'", '\f'.show) - assertEquals("'\\r'", '\r'.show) - assertEquals("'\\''", '\''.show) - assertEquals("'\\\"'", '\"'.show) - assertEquals("'\\\\'", '\\'.show) - } - - @Test def showCar = { - case class Car(model: String, manufacturer: String, year: Int) - implicit val showCar: Show[Car] = new Show[Car] { - def show(c: Car) = - "Car(" + c.model.show + ", " + c.manufacturer.show + ", " + c.year.show + ")" - } - - case class Shop(xs: List[Car], name: String) - implicit val showShop: Show[Shop] = new Show[Shop] { - def show(sh: Shop) = - "Shop(" + sh.xs.show + ", " + sh.name.show + ")" - } - - assertEquals("Car(\"Mustang\", \"Ford\", 1967)", Car("Mustang", "Ford", 1967).show) - } - - @Test def showOptions = { - assertEquals("None", None.show) - val empty = Option.empty - assertEquals("None", empty.show) - assertEquals("None", (None: Option[String]).show) - assertEquals("Some(\"hello opt\")", Some("hello opt").show) - } - - @Test def showMaps = { - val mp = scala.collection.immutable.Map("str1" -> "val1", "str2" -> "val2") - assertEquals("Map(\"str1\" -> \"val1\", \"str2\" -> \"val2\")", mp.show) - } - - @Test def withoutShow = { - case class Car(model: String, manufacturer: String, year: Int) - assertEquals("Car(Mustang,Ford,1967)", Car("Mustang", "Ford", 1967).show) - } - - @Test def partialShow = { - case object Foo - assertEquals("Map(Foo -> \"Hello\")", Map(Foo -> "Hello").show) - } - - @Test def showArrays = { - assertEquals("Array()", Array[Int]().show) - assertEquals("Array(1)", Array(1).show) - assertEquals("Array(1, 2, 3)", Array(1, 2, 3).show) - } - - @Test def showNull = { - assertEquals("null", (null: String).show) - assertEquals("List(null)", List(null).show) - assertEquals("List(null)", List[String](null).show) - } - - @Test def showNothing = { - val emptyMap = Map() - assertEquals("Map()", emptyMap.show) - assertEquals("List()", List().show) - } -} diff --git a/tests/neg/i3537.scala b/tests/neg/i3537.scala index d334dfc776d1..d541345482b4 100644 --- a/tests/neg/i3537.scala +++ b/tests/neg/i3537.scala @@ -1,5 +1,3 @@ -import dotty.Show._ - class Foo(x: Int) object Test {