summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-04-20 12:25:03 -0700
committerPaul Phillips <paulp@improving.org>2013-04-20 14:58:09 -0700
commitc4d0fd9998d96843cd9704d5610a29ab752ecb14 (patch)
treec8bf37f4c1e474b85c859593463a319ff1407104
parent68c6ba7e59befa88b2f383dd7eca71a9329a8b6d (diff)
downloadscala-c4d0fd9998d96843cd9704d5610a29ab752ecb14.tar.gz
scala-c4d0fd9998d96843cd9704d5610a29ab752ecb14.tar.bz2
scala-c4d0fd9998d96843cd9704d5610a29ab752ecb14.zip
-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.
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala92
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--test/files/run/memberpos.check11
-rw-r--r--test/files/run/memberpos.scala39
4 files changed, 131 insertions, 12 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
index 80d70e6428..3a695c6f59 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
@@ -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)
}
}
}
-
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index ee9a3aed2a..a30f144802 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -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)" , "")
diff --git a/test/files/run/memberpos.check b/test/files/run/memberpos.check
new file mode 100644
index 0000000000..e7d3534000
--- /dev/null
+++ b/test/files/run/memberpos.check
@@ -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
+
diff --git a/test/files/run/memberpos.scala b/test/files/run/memberpos.scala
new file mode 100644
index 0000000000..f2b79c0ec1
--- /dev/null
+++ b/test/files/run/memberpos.scala
@@ -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 }
+
+"""
+}