From a8b8814bc9c1993f441b956d82be59354c1ea2a2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 21 Nov 2017 16:58:39 +0100 Subject: [PATCH 1/4] Add -print-lines setting --- .../tools/dotc/config/ScalaSettings.scala | 3 ++ .../tools/dotc/printing/RefinedPrinter.scala | 31 +++++++++++++++---- .../src/dotty/tools/dotc/printing/Texts.scala | 29 +++++++++-------- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 1519a9f8e1ef..9903c6d75519 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -43,7 +43,10 @@ class ScalaSettings extends Settings.SettingGroup { val rewrite = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax") val silentWarnings = BooleanSetting("-nowarn", "Silence all warnings.") val fromTasty = BooleanSetting("-from-tasty", "Compile classes from tasty in classpath. The arguments are used as class names.") + + /** Decompiler settings */ val printTasty = BooleanSetting("-print-tasty", "Prints the raw tasty when decompiling.") + val printLines = BooleanSetting("-print-lines", "Show source code line numbers.") /** -X "Advanced" settings */ diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c9dd0d0046c4..83ff0480b39c 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -16,6 +16,8 @@ import config.Config import transform.SymUtils._ import scala.annotation.switch import language.implicitConversions +import dotty.tools.dotc.util.SourcePosition + class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { @@ -23,6 +25,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { private[this] var enclosingDef: untpd.Tree = untpd.EmptyTree private[this] var myCtx: Context = _ctx private[this] var printPos = ctx.settings.Yprintpos.value + private[this] val printLines = ctx.settings.printLines.value override protected[this] implicit def ctx: Context = myCtx def withEnclosingDef(enclDef: Tree[_ >: Untyped])(op: => Text): Text = { @@ -287,9 +290,17 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (ctx.settings.uniqid.value && tree.hasType && tree.symbol.exists) s"#${tree.symbol.id}" else "" } - def nameIdText(tree: untpd.NameTree): Text = - if (tree.hasType && tree.symbol.exists) nameString(tree.symbol) + def nameIdText(tree: untpd.NameTree): Text = { + if (tree.hasType && tree.symbol.exists) { + val str: Text = nameString(tree.symbol) + tree match { + case tree: RefTree => withPos(str, tree.pos) + case tree: MemberDef => withPos(str, tree.namePos) + case _ => str + } + } else toText(tree.name) ~ idText(tree) + } def toTextTemplate(impl: Template, ofNew: Boolean = false): Text = { val Template(constr @ DefDef(_, tparams, vparamss, _, _), parents, self, _) = impl @@ -343,7 +354,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { tree.typeOpt match { case tp: NamedType if name != nme.WILDCARD => val pre = if (tp.symbol is JavaStatic) tp.prefix.widen else tp.prefix - toTextPrefix(pre) ~ selectionString(tp) + toTextPrefix(pre) ~ withPos(selectionString(tp), tree.pos) case _ => toText(name) } @@ -365,8 +376,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { toTextLocal(fun) ~ "[" ~ toTextGlobal(args, ", ") ~ "]" case Literal(c) => tree.typeOpt match { - case ConstantType(tc) => toText(tc) - case _ => toText(c) + case ConstantType(tc) => withPos(toText(tc), tree.pos) + case _ => withPos(toText(c), tree.pos) } case New(tpt) => "new " ~ { @@ -517,7 +528,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case SymbolLit(str) => "'" + str case InterpolatedString(id, segments) => - def strText(str: Literal) = Str(escapedString(str.const.stringValue)) + def strText(str: Literal) = withPos(escapedString(str.const.stringValue), tree.pos) def segmentText(segment: Tree) = segment match { case Thicket(List(str: Literal, expr)) => strText(str) ~ "{" ~ toTextGlobal(expr) ~ "}" case str: Literal => strText(str) @@ -694,4 +705,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } override def plain = new PlainPrinter(_ctx) + + def withPos(txt: Text, pos: SourcePosition): Text = { + if (!printLines || !pos.exists) txt + else txt match { + case Str(s, _, _) => Str(s, pos.line, pos.endLine) + case _ => txt + } + } } diff --git a/compiler/src/dotty/tools/dotc/printing/Texts.scala b/compiler/src/dotty/tools/dotc/printing/Texts.scala index db81cab7afaa..693e0b41b330 100644 --- a/compiler/src/dotty/tools/dotc/printing/Texts.scala +++ b/compiler/src/dotty/tools/dotc/printing/Texts.scala @@ -1,6 +1,5 @@ package dotty.tools.dotc package printing -import core.Contexts.Context import language.implicitConversions object Texts { @@ -12,7 +11,7 @@ object Texts { def relems: List[Text] def isEmpty: Boolean = this match { - case Str(s) => s.isEmpty + case Str(s, _, _) => s.isEmpty case Fluid(relems) => relems forall (_.isEmpty) case Vertical(relems) => relems.isEmpty } @@ -25,7 +24,7 @@ object Texts { def close = new Closed(relems) def remaining(width: Int): Int = this match { - case Str(s) => + case Str(s, _, _) => width - s.length case Fluid(Nil) => width @@ -37,15 +36,15 @@ object Texts { } def lastLine: String = this match { - case Str(s) => s + case Str(s, _, _) => s case _ => relems.head.lastLine } def appendToLastLine(that: Text): Text = that match { - case Str(s2) => + case Str(s2, start1, end1) => this match { - case Str(s1) => Str(s1 + s2) - case Fluid(Str(s1) :: prev) => Fluid(Str(s1 + s2) :: prev) + case Str(s1, start2, end2) => Str(s1 + s2, start1 min start2, end1 max end2) + case Fluid(Str(s1, start2, end2) :: prev) => Fluid(Str(s1 + s2, start1 min start2, end1 max end2) :: prev) case Fluid(relems) => Fluid(that :: relems) } case Fluid(relems) => @@ -66,7 +65,7 @@ object Texts { } def layout(width: Int): Text = this match { - case Str(_) => + case Str(s, _, _) => this case Fluid(relems) => ((Str(""): Text) /: relems.reverse)(_.append(width)(_)) @@ -75,13 +74,13 @@ object Texts { } def map(f: String => String): Text = this match { - case Str(s) => Str(f(s)) + case Str(s, start, end) => Str(f(s), start, end) case Fluid(relems) => Fluid(relems map (_ map f)) case Vertical(relems) => Vertical(relems map (_ map f)) } def stripPrefix(pre: String): Text = this match { - case Str(s) => + case Str(s, _, _) => if (s.startsWith(pre)) s drop pre.length else s case Fluid(relems) => val elems = relems.reverse @@ -94,14 +93,18 @@ object Texts { } private def indented: Text = this match { - case Str(s) => Str((" " * indentMargin) + s) + case Str(s, start, end) => Str((" " * indentMargin) + s, start, end) case Fluid(relems) => Fluid(relems map (_.indented)) case Vertical(relems) => Vertical(relems map (_.indented)) } def print(sb: StringBuilder): Unit = this match { - case Str(s) => + case Str(s, start, end) => sb.append(s) + if (start == end) + sb.append(s" // @line ${start + 1}") + else if (start != Int.MaxValue) + sb.append(s" // @lines ${start + 1} to ${end + 1}") case _ => var follow = false for (elem <- relems.reverse) { @@ -155,7 +158,7 @@ object Texts { def lines(xs: Traversable[Text]) = Vertical(xs.toList.reverse) } - case class Str(s: String) extends Text { + case class Str(s: String, startLine: Int = Int.MaxValue, endLine: Int = -1) extends Text { override def relems: List[Text] = List(this) } From d82d4ad343fa7f27814211fa8f7319ae92099a54 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 21 Nov 2017 17:19:41 +0100 Subject: [PATCH 2/4] Put lines at the start of the line and respect wageWidth --- .../dotty/tools/dotc/core/Decorators.scala | 2 +- .../src/dotty/tools/dotc/printing/Texts.scala | 29 ++++++++++++++----- compiler/src/dotty/tools/repl/package.scala | 2 +- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 53b2e19e5e12..094d4092ac1a 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -143,7 +143,7 @@ object Decorators { } implicit class TextToString(val text: Text) extends AnyVal { - def show(implicit ctx: Context) = text.mkString(ctx.settings.pageWidth.value) + def show(implicit ctx: Context) = text.mkString(ctx.settings.pageWidth.value, ctx.settings.printLines.value) } /** Test whether a list of strings representing phases contains diff --git a/compiler/src/dotty/tools/dotc/printing/Texts.scala b/compiler/src/dotty/tools/dotc/printing/Texts.scala index 693e0b41b330..31e30050527b 100644 --- a/compiler/src/dotty/tools/dotc/printing/Texts.scala +++ b/compiler/src/dotty/tools/dotc/printing/Texts.scala @@ -98,25 +98,38 @@ object Texts { case Vertical(relems) => Vertical(relems map (_.indented)) } - def print(sb: StringBuilder): Unit = this match { + def print(sb: StringBuilder, numberWidth: Int): Unit = this match { case Str(s, start, end) => + def printLine(ln: String) = { + val pad = (numberWidth - ln.length - 1) + assert(pad >= 0) + sb.append(" " * pad) + sb.append(ln) + sb.append("|") + } + if (numberWidth == 0) () // Do not print line numbers + else if (start == end) printLine((start + 1).toString) + else if (start != Int.MaxValue) printLine(s"${start + 1}-${end + 1}") + else printLine("") sb.append(s) - if (start == end) - sb.append(s" // @line ${start + 1}") - else if (start != Int.MaxValue) - sb.append(s" // @lines ${start + 1} to ${end + 1}") case _ => var follow = false for (elem <- relems.reverse) { if (follow) sb.append("\n") - elem.print(sb) + elem.print(sb, numberWidth) follow = true } } - def mkString(width: Int): String = { + def maxLine: Int = this match { + case Str(_, _, end) => end + case _ => (-1 /: relems)((acc, relem) => acc max relem.maxLine) + } + + def mkString(width: Int, withLineNumbers: Boolean): String = { val sb = new StringBuilder - layout(width).print(sb) + val numberWidth = if (withLineNumbers) (2 * maxLine.toString.length) + 2 else 0 + layout(width - numberWidth).print(sb, numberWidth) sb.toString } diff --git a/compiler/src/dotty/tools/repl/package.scala b/compiler/src/dotty/tools/repl/package.scala index 4d73926647cd..7643a6a95102 100644 --- a/compiler/src/dotty/tools/repl/package.scala +++ b/compiler/src/dotty/tools/repl/package.scala @@ -22,7 +22,7 @@ package object repl { def showUser(implicit ctx: Context): String = { val printer = new UserFacingPrinter(ctx) val text = printer.dclText(s) - text.mkString(ctx.settings.pageWidth.value) + text.mkString(ctx.settings.pageWidth.value, ctx.settings.printLines.value) } } From ca34ba0fea6c65c4f4fa49726a21593ba04d7d2b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 22 Nov 2017 13:12:43 +0100 Subject: [PATCH 3/4] Create Texts.LineRange and use it in Texts.Str --- .../tools/dotc/printing/RefinedPrinter.scala | 2 +- .../src/dotty/tools/dotc/printing/Texts.scala | 44 +++++++++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 83ff0480b39c..965596d5c1af 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -709,7 +709,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def withPos(txt: Text, pos: SourcePosition): Text = { if (!printLines || !pos.exists) txt else txt match { - case Str(s, _, _) => Str(s, pos.line, pos.endLine) + case Str(s, _) => Str(s, LineRange(pos.line, pos.endLine)) case _ => txt } } diff --git a/compiler/src/dotty/tools/dotc/printing/Texts.scala b/compiler/src/dotty/tools/dotc/printing/Texts.scala index 31e30050527b..382e03bce568 100644 --- a/compiler/src/dotty/tools/dotc/printing/Texts.scala +++ b/compiler/src/dotty/tools/dotc/printing/Texts.scala @@ -11,7 +11,7 @@ object Texts { def relems: List[Text] def isEmpty: Boolean = this match { - case Str(s, _, _) => s.isEmpty + case Str(s, _) => s.isEmpty case Fluid(relems) => relems forall (_.isEmpty) case Vertical(relems) => relems.isEmpty } @@ -24,7 +24,7 @@ object Texts { def close = new Closed(relems) def remaining(width: Int): Int = this match { - case Str(s, _, _) => + case Str(s, _) => width - s.length case Fluid(Nil) => width @@ -36,15 +36,15 @@ object Texts { } def lastLine: String = this match { - case Str(s, _, _) => s + case Str(s, _) => s case _ => relems.head.lastLine } def appendToLastLine(that: Text): Text = that match { - case Str(s2, start1, end1) => + case Str(s2, lines1) => this match { - case Str(s1, start2, end2) => Str(s1 + s2, start1 min start2, end1 max end2) - case Fluid(Str(s1, start2, end2) :: prev) => Fluid(Str(s1 + s2, start1 min start2, end1 max end2) :: prev) + case Str(s1, lines2) => Str(s1 + s2, lines1 union lines2) + case Fluid(Str(s1, lines2) :: prev) => Fluid(Str(s1 + s2, lines1 union lines2) :: prev) case Fluid(relems) => Fluid(that :: relems) } case Fluid(relems) => @@ -65,7 +65,7 @@ object Texts { } def layout(width: Int): Text = this match { - case Str(s, _, _) => + case Str(s, _) => this case Fluid(relems) => ((Str(""): Text) /: relems.reverse)(_.append(width)(_)) @@ -74,13 +74,13 @@ object Texts { } def map(f: String => String): Text = this match { - case Str(s, start, end) => Str(f(s), start, end) + case Str(s, lines) => Str(f(s), lines) case Fluid(relems) => Fluid(relems map (_ map f)) case Vertical(relems) => Vertical(relems map (_ map f)) } def stripPrefix(pre: String): Text = this match { - case Str(s, _, _) => + case Str(s, _) => if (s.startsWith(pre)) s drop pre.length else s case Fluid(relems) => val elems = relems.reverse @@ -93,24 +93,21 @@ object Texts { } private def indented: Text = this match { - case Str(s, start, end) => Str((" " * indentMargin) + s, start, end) + case Str(s, lines) => Str((" " * indentMargin) + s, lines) case Fluid(relems) => Fluid(relems map (_.indented)) case Vertical(relems) => Vertical(relems map (_.indented)) } def print(sb: StringBuilder, numberWidth: Int): Unit = this match { - case Str(s, start, end) => - def printLine(ln: String) = { + case Str(s, lines) => + if (numberWidth != 0) { + val ln = lines.show val pad = (numberWidth - ln.length - 1) assert(pad >= 0) sb.append(" " * pad) sb.append(ln) sb.append("|") } - if (numberWidth == 0) () // Do not print line numbers - else if (start == end) printLine((start + 1).toString) - else if (start != Int.MaxValue) printLine(s"${start + 1}-${end + 1}") - else printLine("") sb.append(s) case _ => var follow = false @@ -122,7 +119,7 @@ object Texts { } def maxLine: Int = this match { - case Str(_, _, end) => end + case Str(_, lines) => lines.end case _ => (-1 /: relems)((acc, relem) => acc max relem.maxLine) } @@ -171,7 +168,7 @@ object Texts { def lines(xs: Traversable[Text]) = Vertical(xs.toList.reverse) } - case class Str(s: String, startLine: Int = Int.MaxValue, endLine: Int = -1) extends Text { + case class Str(s: String, lineRange: LineRange = EmptyLineRange) extends Text { override def relems: List[Text] = List(this) } @@ -181,4 +178,15 @@ object Texts { class Closed(relems: List[Text]) extends Fluid(relems) implicit def stringToText(s: String): Text = Str(s) + + /** Inclusive line range */ + case class LineRange(start: Int, end: Int) { + def union(that: LineRange): LineRange = LineRange(start min that.start, end max that.end) + def show: String = + if (start == end) (start + 1).toString + else if (start < end) s"${start + 1}-${end + 1}" + else "" // empty range + } + + object EmptyLineRange extends LineRange(Int.MaxValue, Int.MinValue) } From 8a5032074806ae0eb6e996099abf182144f10621 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 23 Nov 2017 08:05:24 +0100 Subject: [PATCH 4/4] Make withPos private --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 965596d5c1af..4a83041e35ae 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -706,7 +706,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override def plain = new PlainPrinter(_ctx) - def withPos(txt: Text, pos: SourcePosition): Text = { + private def withPos(txt: Text, pos: SourcePosition): Text = { if (!printLines || !pos.exists) txt else txt match { case Str(s, _) => Str(s, LineRange(pos.line, pos.endLine))