Permalink
Browse files

-Yshow-member-pos, print the positions of members.

Here for instance is a command line which leverages the
output of this option to print the method bodies of all
methods called 'transformInfo' found under src. Given
1500 source files it accomplishes this in four seconds,
thanks to -Ystop-after:parser.

% scalac -Yshow-member-pos sed -Ystop-after:parser \
    $(find src/compiler -name '*.scala') | \
    grep transformInfo | sed 's/ # .*//;' | \
    while read line; do echo "// $line" && gsed -n $line && echo; done

Or more simply, the start/end lines of each member of Random:

% scalac -Yshow-member-pos "" ./src/library/scala/util/Random.scala
./src/library/scala/util/Random.scala
20,134               class Random
 33                  def nextBoolean
 38                  def nextBytes
 43                  def nextDouble
 48                  def nextFloat
 54                  def nextGaussian
 59                  def nextInt
 65                  def nextInt
 70                  def nextLong
 81,89               def nextString
  82,86              def safeChar
   83                val surrogateStart
   84                val res
 94,98               def nextPrintableChar
 [snip]

It makes me sad I'm always in the position of having to hack
the compiler to do this sort of thing. All we need is something
like -Yinsert-phase:Foo where Foo is a class implementing a
(Phase, Tree) => Tree method, and the compiler runs all the
unit.bodies through it after each phase, and then one could
easily accomplish this in the privacy of one's own compiler.
  • Loading branch information...
1 parent 68c6ba7 commit c4d0fd9998d96843cd9704d5610a29ab752ecb14 @paulp paulp committed Apr 20, 2013
@@ -11,30 +11,98 @@ import javac._
/** An nsc sub-component.
*/
abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParsers with Scanners with JavaParsers with JavaScanners {
+ import global._
val phaseName = "parser"
-
def newPhase(prev: Phase): StdPhase = new ParserPhase(prev)
- class ParserPhase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) {
+ abstract class MemberDefTraverser extends Traverser {
+ def onMember(defn: MemberDef): Unit
+
+ private var depth: Int = 0
+ private def lower[T](body: => T): T = {
+ depth += 1
+ try body finally depth -= 1
+ }
+ def currentDepth = depth
+
+ /** Prune this tree and all trees beneath it. Can be overridden. */
+ def prune(md: MemberDef): Boolean = (
+ md.mods.isSynthetic
+ || md.mods.isParamAccessor
+ || nme.isConstructorName(md.name)
+ || (md.name containsName nme.ANON_CLASS_NAME)
+ )
+
+ override def traverse(t: Tree): Unit = t match {
+ case md: MemberDef if prune(md) =>
+ case md @ PackageDef(_, stats) => traverseTrees(stats)
+ case md: ImplDef => onMember(md) ; lower(traverseTrees(md.impl.body))
+ case md: ValOrDefDef => onMember(md) ; lower(traverse(md.rhs))
+ case _ => super.traverse(t)
+ }
+ }
+
+ class MemberPosReporter(unit: CompilationUnit) extends MemberDefTraverser {
+ private var outputFn: MemberDef => String = outputForScreen
+ val path = unit.source.file.path
+
+ // If a single line, outputs the line; if it spans multiple lines
+ // outputs NN,NN with start and end lines, e.g. 15,25.
+ def outputPos(md: MemberDef): String = {
+ val pos = md.pos
+ val start = pos.focusStart.line
+ val end = pos.focusEnd.line
+
+ if (start == end) "" + start else s"$start,$end"
+ }
+ def outputForSed(md: MemberDef): String = {
+ val pos_s = "%-12s" format outputPos(md) + "p"
+ s"$pos_s $path # ${md.keyword} ${md.name}"
+ }
+ def outputForScreen(md: MemberDef): String = {
+ val pos_s = "%-20s" format " " * currentDepth + outputPos(md)
+ s"$pos_s ${md.keyword} ${md.name}"
+ }
+
+ def onMember(md: MemberDef) = println(outputFn(md))
+ // It recognizes "sed" and "anything else".
+ def show(style: String) {
+ if (style == "sed") {
+ outputFn = outputForSed
+ traverse(unit.body)
+ }
+ else {
+ outputFn = outputForScreen
+ println(path)
+ traverse(unit.body)
+ }
+ println("")
+ }
+ }
+
+ private def initialUnitBody(unit: CompilationUnit): Tree = {
+ if (unit.isJava) new JavaUnitParser(unit).parse()
+ else if (global.reporter.incompleteHandled) newUnitParser(unit).parse()
+ else newUnitParser(unit).smartParse()
+ }
+
+ class ParserPhase(prev: Phase) extends StdPhase(prev) {
override val checkable = false
override val keepsTypeParams = false
- def apply(unit: global.CompilationUnit) {
- import global._
+ def apply(unit: CompilationUnit) {
informProgress("parsing " + unit)
- // if the body is already filled in, do nothing
+ // if the body is already filled in, don't overwrite it
// otherwise compileLate is going to overwrite bodies of synthetic source files
- if (unit.body == EmptyTree) {
- unit.body =
- if (unit.isJava) new JavaUnitParser(unit).parse()
- else if (reporter.incompleteHandled) newUnitParser(unit).parse()
- else newUnitParser(unit).smartParse()
- }
+ if (unit.body == EmptyTree)
+ unit.body = initialUnitBody(unit)
if (settings.Yrangepos && !reporter.hasErrors)
validatePositions(unit.body)
+
+ if (settings.Ymemberpos.isSetByUser)
+ new MemberPosReporter(unit) show (style = settings.Ymemberpos.value)
}
}
}
-
@@ -164,6 +164,7 @@ trait ScalaSettings extends AbsScalaSettings
val refinementMethodDispatch
= ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache")
val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.")
+ val Ymemberpos = StringSetting ("-Yshow-member-pos", "output style", "Show start and end positions of members", "") withPostSetHook (_ => Yrangepos.value = true)
val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")
val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup")
val Yreploutdir = StringSetting ("-Yrepl-outdir", "path", "Write repl-generated classfiles to given output directory (use \"\" to generate a temporary dir)" , "")
@@ -0,0 +1,11 @@
+newSource1
+2,4 class A
+6,28 object A
+ 7,10 def bippy
+ 8 def hello
+ 11,27 class Dingo
+ 12,26 def foooooz
+ 22 val a
+30 class B
+ 30 def f
+
@@ -0,0 +1,39 @@
+import scala.tools.partest._
+
+// Simple sanity test for -Yshow-member-pos.
+object Test extends DirectTest {
+ override def extraSettings: String = "-usejavacp -Ystop-after:parser -Yshow-member-pos \"\" -d " + testOutput.path
+ override def show() = compile()
+ override def code = """
+class A(val a: Int = 1) {
+
+}
+
+object A {
+ def bippy = {
+ def hello = 55
+ "" + hello
+ }
+ class Dingo {
+ def foooooz = /****
+
+
+
+
+
+ ****/ {
+
+
+
+ val a = 1
+
+
+ a
+ }
+ }
+}
+
+class B { def f = 1 }
+
+"""
+}

0 comments on commit c4d0fd9

Please sign in to comment.