From 202eb73b6cd6ebb3e20ff9f0a198c4ea83319851 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 28 Jan 2014 12:23:47 +0300 Subject: adds showDeclaration(sym: Symbol): String MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per Paul’s request, this commit exposes Symbol.defString, although in a different way to ensure consistency with our other prettyprinting facilities provided in the reflection API. --- src/reflect/scala/reflect/api/Printers.scala | 7 ++- src/reflect/scala/reflect/internal/Printers.scala | 51 ++++++++++++---------- .../scala/reflect/runtime/JavaMirrors.scala | 33 ++------------ .../files/run/reflection-allmirrors-tostring.check | 18 ++++---- .../files/run/reflection-magicsymbols-invoke.check | 12 ++--- test/files/run/showdecl.check | 34 +++++++++++++++ test/files/run/showdecl/Macros_1.scala | 30 +++++++++++++ test/files/run/showdecl/Test_2.scala | 32 ++++++++++++++ 8 files changed, 149 insertions(+), 68 deletions(-) create mode 100644 test/files/run/showdecl.check create mode 100644 test/files/run/showdecl/Macros_1.scala create mode 100644 test/files/run/showdecl/Test_2.scala diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala index 5bc92d3893..ae1ad30527 100644 --- a/src/reflect/scala/reflect/api/Printers.scala +++ b/src/reflect/scala/reflect/api/Printers.scala @@ -219,7 +219,7 @@ trait Printers { self: Universe => * @group Printers */ protected def newCodePrinter(out: PrintWriter): TreePrinter - + /** Renders internal structure of a reflection artifact as the * visualization of a Scala syntax tree. * @@ -252,4 +252,9 @@ trait Printers { self: Universe => * @group Printers */ def showRaw(flags: FlagSet): String = flags.toString + + /** Renders a string that represents a declaration of this symbol written in Scala. + * @group Printers + */ + def showDeclaration(sym: Symbol): String } diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 519d1047a6..b287a3884a 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -128,10 +128,10 @@ trait Printers extends api.Printers { self: SymbolTable => body if (condition) print(")") } - - protected def printImplicitInParamsList(vds: List[ValDef]) = + + protected def printImplicitInParamsList(vds: List[ValDef]) = if (vds.nonEmpty) printFlags(vds.head.mods.flags & IMPLICIT, "") - + def printValueParams(ts: List[ValDef], inParentheses: Boolean = true): Unit = parenthesize(inParentheses){ printImplicitInParamsList(ts) @@ -191,7 +191,7 @@ trait Printers extends api.Printers { self: SymbolTable => private var currentOwner: Symbol = NoSymbol private var selectorType: Type = NoType - + protected def printPackageDef(tree: PackageDef, separator: String) = { val PackageDef(packaged, stats) = tree printAnnotations(tree) @@ -511,7 +511,7 @@ trait Printers extends api.Printers { self: SymbolTable => out.print(if (arg == null) "null" else arg.toString) } } - + // it's the printer for trees after parser and before typer phases class ParsedTreePrinter(out: PrintWriter) extends TreePrinter(out) { override def withTypes = this @@ -537,13 +537,13 @@ trait Printers extends api.Printers { self: SymbolTable => import Chars._ val decName = name.decoded val bslash = '\\' - val brackets = List('[',']','(',')','{','}') + val brackets = List('[',']','(',')','{','}') def addBackquotes(s: String) = - if (decoded && (decName.exists(ch => brackets.contains(ch) || isWhitespace(ch)) || + if (decoded && (decName.exists(ch => brackets.contains(ch) || isWhitespace(ch)) || (name.isOperatorName && decName.exists(isOperatorPart) && decName.exists(isScalaLetter) && !decName.contains(bslash)))) s"`$s`" else s - + if (name == nme.CONSTRUCTOR) "this" else addBackquotes(quotedName(name, decoded)) } @@ -556,7 +556,7 @@ trait Printers extends api.Printers { self: SymbolTable => qualIsIntLit && name.isOperatorName } - protected def needsParentheses(parent: Tree)(insideIf: Boolean = true, insideMatch: Boolean = true, + protected def needsParentheses(parent: Tree)(insideIf: Boolean = true, insideMatch: Boolean = true, insideTry: Boolean = true, insideAnnotated: Boolean = true, insideBlock: Boolean = true, insideLabelDef: Boolean = true) = { parent match { case _: If => insideIf @@ -572,10 +572,10 @@ trait Printers extends api.Printers { self: SymbolTable => protected def checkForBlank(cond: Boolean) = if (cond) " " else "" protected def blankForOperatorName(name: Name) = checkForBlank(name.isOperatorName) protected def blankForName(name: Name) = checkForBlank(name.isOperatorName || name.endsWith("_")) - + protected def resolveSelect(t: Tree): String = { t match { - // case for: 1) (if (a) b else c).meth1.meth2 or 2) 1 + 5 should be represented as (1).+(5) + // case for: 1) (if (a) b else c).meth1.meth2 or 2) 1 + 5 should be represented as (1).+(5) case Select(qual, name) if (name.isTermName && needsParentheses(qual)(insideLabelDef = false)) || isIntLitWithDecodedOp(qual, name) => s"(${resolveSelect(qual)}).${printedName(name)}" case Select(qual, name) if name.isTermName => s"${resolveSelect(qual)}.${printedName(name)}" case Select(qual, name) if name.isTypeName => s"${resolveSelect(qual)}#${blankForOperatorName(name)}%${printedName(name)}" @@ -591,7 +591,7 @@ trait Printers extends api.Printers { self: SymbolTable => trees match { case Nil => trees case init :+ last => last match { - case Select(Ident(sc), name) if traitsToRemove.contains(name) && sc == nme.scala_ => + case Select(Ident(sc), name) if traitsToRemove.contains(name) && sc == nme.scala_ => removeDefaultTraitsFromList(init, traitsToRemove) case _ => trees } @@ -637,7 +637,7 @@ trait Printers extends api.Printers { self: SymbolTable => val mutableOrOverride = mods.isOverride || mods.isMutable val hideCtorMods = mods.isParamAccessor && mods.isPrivateLocal && !mutableOrOverride val hideCaseCtorMods = mods.isCaseAccessor && mods.isPublic && !mutableOrOverride - + if (primaryCtorParam && !(hideCtorMods || hideCaseCtorMods)) { printModifiers(mods, primaryCtorParam) print(if (mods.isMutable) "var " else "val "); @@ -657,14 +657,14 @@ trait Printers extends api.Printers { self: SymbolTable => printParam(tree, primaryCtorParam = false) } - protected def printArgss(argss: List[List[Tree]]) = + protected def printArgss(argss: List[List[Tree]]) = argss foreach {x: List[Tree] => if (!(x.isEmpty && argss.size == 1)) printRow(x, "(", ", ", ")")} - + override def printAnnotations(tree: MemberDef) = { val annots = tree.mods.annotations annots foreach {annot => printAnnot(annot); print(" ")} } - + protected def printAnnot(tree: Tree) = { tree match { case treeInfo.Applied(core, _, argss) => @@ -675,10 +675,10 @@ trait Printers extends api.Printers { self: SymbolTable => } printArgss(argss) case _ => super.printTree(tree) - } + } } - override def printTree(tree: Tree): Unit = { + override def printTree(tree: Tree): Unit = { parentsStack.push(tree) tree match { case cl @ ClassDef(mods, name, tparams, impl) => @@ -809,15 +809,15 @@ trait Printers extends api.Printers { self: SymbolTable => } case _ => None } - + if (printedParents.nonEmpty) { val (clParent :: traits) = printedParents print(clParent) val constrArgss = ap match { case Some(treeInfo.Applied(_, _, argss)) => argss - case _ => Nil - } + case _ => Nil + } printArgss(constrArgss) if (traits.nonEmpty) { printRow(traits, " with ", " with ", "") @@ -907,7 +907,7 @@ trait Printers extends api.Printers { self: SymbolTable => case Apply(fun, vargs) => tree match { // processing methods ending on colons (x \: list) - case Apply(Block(l1 @ List(sVD: ValDef), a1 @ Apply(Select(_, methodName), l2 @ List(Ident(iVDName)))), l3) + case Apply(Block(l1 @ List(sVD: ValDef), a1 @ Apply(Select(_, methodName), l2 @ List(Ident(iVDName)))), l3) if sVD.mods.isSynthetic && treeInfo.isLeftAssoc(methodName) && sVD.name == iVDName => val printBlock = Block(l1, Apply(a1, l3)) print(printBlock) @@ -972,7 +972,7 @@ trait Printers extends api.Printers { self: SymbolTable => case AppliedTypeTree(tp, args) => // it's possible to have (=> String) => String type but Function1[=> String, String] is not correct val containsByNameTypeParam = args exists treeInfo.isByNameParamType - + if (containsByNameTypeParam) { print("(") printRow(args.init, "(", ", ", ")") @@ -1237,4 +1237,9 @@ trait Printers extends api.Printers { self: SymbolTable => s_flags mkString " | " } } + + def showDeclaration(sym: Symbol): String = { + if (!isCompilerUniverse) definitions.fullyInitializeSymbol(sym) + sym.defString + } } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index bf8a3f0ae2..1e64b805e9 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -292,32 +292,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive jfield.set(receiver, if (isDerivedValueClass) unboxer.invoke(value) else value) } - override def toString = s"field mirror for ${symbol.fullName} (bound to $receiver)" - } - - private def showMethodSig(symbol: MethodSymbol): String = { - var sig = s"${symbol.fullName}" - if (symbol.typeParams.nonEmpty) { - def showTparam(tparam: Symbol) = - tparam.typeSignature match { - case tpe @ TypeBounds(_, _) => s"${tparam.name}$tpe" - case _ => tparam.name - } - def showTparams(tparams: List[Symbol]) = "[" + (tparams map showTparam mkString ", ") + "]" - sig += showTparams(symbol.typeParams) - } - if (symbol.paramss.nonEmpty) { - def showParam(param: Symbol) = s"${param.name}: ${param.typeSignature}" - def showParams(params: List[Symbol]) = { - val s_mods = if (params.nonEmpty && params(0).hasFlag(IMPLICIT)) "implicit " else "" - val s_params = params map showParam mkString ", " - "(" + s_mods + s_params + ")" - } - def showParamss(paramss: List[List[Symbol]]) = paramss map showParams mkString "" - sig += showParamss(symbol.paramss) - } - sig += s": ${symbol.returnType}" - sig + override def toString = s"field mirror for ${showDeclaration(symbol)} (bound to $receiver)" } // the "symbol == Any_getClass || symbol == Object_getClass" test doesn't cut it @@ -372,7 +347,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive override def toString = { val what = if (symbol.isConstructor) "constructor mirror" else "method mirror" - s"$what for ${showMethodSig(symbol)} (bound to $receiver)" + s"$what for ${showDeclaration(symbol)} (bound to $receiver)" } } @@ -468,7 +443,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive private class BytecodelessMethodMirror[T: ClassTag](val receiver: T, val symbol: MethodSymbol) extends MethodMirror { def bind(newReceiver: Any) = new BytecodelessMethodMirror(newReceiver.asInstanceOf[T], symbol) - override def toString = s"bytecodeless method mirror for ${showMethodSig(symbol)} (bound to $receiver)" + override def toString = s"bytecodeless method mirror for ${showDeclaration(symbol)} (bound to $receiver)" def apply(args: Any*): Any = { // checking type conformance is too much of a hassle, so we don't do it here @@ -482,7 +457,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive if (!perfectMatch && !varargMatch) { val n_arguments = if (isVarArgsList(params)) s"${params.length - 1} or more" else s"${params.length}" val s_arguments = if (params.length == 1 && !isVarArgsList(params)) "argument" else "arguments" - abort(s"${showMethodSig(symbol)} takes $n_arguments $s_arguments") + abort(s"${showDeclaration(symbol)} takes $n_arguments $s_arguments") } def objReceiver = receiver.asInstanceOf[AnyRef] diff --git a/test/files/run/reflection-allmirrors-tostring.check b/test/files/run/reflection-allmirrors-tostring.check index 2a3be29402..3003cce6c0 100644 --- a/test/files/run/reflection-allmirrors-tostring.check +++ b/test/files/run/reflection-allmirrors-tostring.check @@ -1,14 +1,14 @@ class mirror for C (bound to null) module mirror for M (bound to null) instance mirror for an instance of C -field mirror for C.f1 (bound to an instance of C) -field mirror for C.f2 (bound to an instance of C) -method mirror for C.m1: Int (bound to an instance of C) -method mirror for C.m2(): Int (bound to an instance of C) -method mirror for C.m3[T >: String <: Int]: T (bound to an instance of C) -method mirror for C.m4[A, B <: A[Int]](x: A[B])(implicit y: Int): Nothing (bound to an instance of C) -method mirror for C.m5(x: => Int, y: Int*): String (bound to an instance of C) +field mirror for private[this] val f1: Int (bound to an instance of C) +field mirror for private[this] var f2: Int (bound to an instance of C) +method mirror for def m1: Int (bound to an instance of C) +method mirror for def m2(): Int (bound to an instance of C) +method mirror for def m3[T >: String <: Int]: T (bound to an instance of C) +method mirror for def m4[A[_], B <: A[Int]](x: A[B])(implicit y: Int): Nothing (bound to an instance of C) +method mirror for def m5(x: => Int,y: Int*): String (bound to an instance of C) class mirror for C.C (bound to an instance of C) module mirror for C.M (bound to an instance of C) -constructor mirror for C.(): C (bound to null) -constructor mirror for C.C.(): C.this.C (bound to an instance of C) +constructor mirror for def (): C (bound to null) +constructor mirror for def (): C.this.C (bound to an instance of C) diff --git a/test/files/run/reflection-magicsymbols-invoke.check b/test/files/run/reflection-magicsymbols-invoke.check index b153ae0470..f580296ae7 100644 --- a/test/files/run/reflection-magicsymbols-invoke.check +++ b/test/files/run/reflection-magicsymbols-invoke.check @@ -15,12 +15,12 @@ testing Any.!=: false testing Any.##: 50 testing Any.==: true testing Any.asInstanceOf: class scala.ScalaReflectionException: Any.asInstanceOf requires a type argument, it cannot be invoked with mirrors -testing Any.asInstanceOf: class scala.ScalaReflectionException: scala.Any.asInstanceOf[T0]: T0 takes 0 arguments +testing Any.asInstanceOf: class scala.ScalaReflectionException: final def asInstanceOf[T0]: T0 takes 0 arguments testing Any.equals: true testing Any.getClass: class java.lang.String testing Any.hashCode: 50 testing Any.isInstanceOf: class scala.ScalaReflectionException: Any.isInstanceOf requires a type argument, it cannot be invoked with mirrors -testing Any.isInstanceOf: class scala.ScalaReflectionException: scala.Any.isInstanceOf[T0]: Boolean takes 0 arguments +testing Any.isInstanceOf: class scala.ScalaReflectionException: final def isInstanceOf[T0]: Boolean takes 0 arguments testing Any.toString: 2 ============ AnyVal @@ -28,7 +28,7 @@ it's important to print the list of AnyVal's members if some of them change (possibly, adding and/or removing magic symbols), we must update this test constructor AnyVal: ()AnyVal method getClass: ()Class[_ <: AnyVal] -testing AnyVal.: class scala.ScalaReflectionException: unsupported symbol constructor AnyVal when invoking bytecodeless method mirror for scala.AnyVal.(): AnyVal (bound to null) +testing AnyVal.: class scala.ScalaReflectionException: unsupported symbol constructor AnyVal when invoking bytecodeless method mirror for def (): AnyVal (bound to null) testing AnyVal.getClass: class scala.ScalaReflectionException: expected a member of class Integer, you provided method scala.AnyVal.getClass ============ AnyRef @@ -59,9 +59,9 @@ method wait: (x$1: Long, x$2: Int)Unit testing Object.!=: false testing Object.##: 50 testing Object.$asInstanceOf: class scala.ScalaReflectionException: AnyRef.$asInstanceOf is an internal method, it cannot be invoked with mirrors -testing Object.$asInstanceOf: class scala.ScalaReflectionException: java.lang.Object.$asInstanceOf[T0](): T0 takes 0 arguments +testing Object.$asInstanceOf: class scala.ScalaReflectionException: final def $asInstanceOf[T0](): T0 takes 0 arguments testing Object.$isInstanceOf: class scala.ScalaReflectionException: AnyRef.$isInstanceOf is an internal method, it cannot be invoked with mirrors -testing Object.$isInstanceOf: class scala.ScalaReflectionException: java.lang.Object.$isInstanceOf[T0](): Boolean takes 0 arguments +testing Object.$isInstanceOf: class scala.ScalaReflectionException: final def $isInstanceOf[T0](): Boolean takes 0 arguments testing Object.==: true testing Object.clone: class java.lang.CloneNotSupportedException: java.lang.String testing Object.eq: true @@ -115,5 +115,5 @@ testing String.+: 23 ============ CTM testing Predef.classOf: class scala.ScalaReflectionException: Predef.classOf is a compile-time function, it cannot be invoked with mirrors -testing Predef.classOf: class scala.ScalaReflectionException: scala.Predef.classOf[T]: Class[T] takes 0 arguments +testing Predef.classOf: class scala.ScalaReflectionException: def classOf[T]: Class[T] takes 0 arguments testing Universe.reify: class scala.ScalaReflectionException: scala.reflect.api.Universe.reify is a macro, i.e. a compile-time function, it cannot be invoked with mirrors diff --git a/test/files/run/showdecl.check b/test/files/run/showdecl.check new file mode 100644 index 0000000000..b8d7f94c57 --- /dev/null +++ b/test/files/run/showdecl.check @@ -0,0 +1,34 @@ +compile-time +uninitialized D: class D extends +initialized D: class D extends C +uninitialized x: val x: +initialized x: val x: Int +uninitialized y: lazy val y: +initialized y: lazy val y: Int +uninitialized z: def z: +initialized z: def z: Int +uninitialized t: def t: +initialized t: def t[T <: Int](x: D)(y: x.W): Int +uninitialized W: type W = String +initialized W: type W = String +uninitialized C: class C extends +initialized C: class C extends D +uninitialized O: object O +initialized O: object O +runtime +autoinitialized D: class D extends C +autoinitialized D: class D extends C +autoinitialized x: val x: Int +autoinitialized x: val x: Int +autoinitialized y: lazy val y: Int +autoinitialized y: lazy val y: Int +autoinitialized z: def z: Int +autoinitialized z: def z: Int +autoinitialized t: def t[T <: Int](x: D)(y: x.W): Int +autoinitialized t: def t[T <: Int](x: D)(y: x.W): Int +autoinitialized W: type W = String +autoinitialized W: type W = String +autoinitialized C: class C extends D +autoinitialized C: class C extends D +autoinitialized O: object O +autoinitialized O: object O diff --git a/test/files/run/showdecl/Macros_1.scala b/test/files/run/showdecl/Macros_1.scala new file mode 100644 index 0000000000..d0493fb97f --- /dev/null +++ b/test/files/run/showdecl/Macros_1.scala @@ -0,0 +1,30 @@ +import scala.reflect.macros.whitebox._ +import scala.language.experimental.macros + +object Macros { + def impl(c: Context) = { + var messages = List[String]() + def println(msg: String) = messages :+= msg + + import c.universe._ + def test(sym: Symbol): Unit = { + println(s"uninitialized ${sym.name}: ${showDeclaration(sym)}") + sym.typeSignature + println(s"initialized ${sym.name}: ${showDeclaration(sym)}") + } + + println("compile-time") + test(c.mirror.staticClass("D")) + test(c.mirror.staticClass("D").typeSignature.member(TermName("x"))) + test(c.mirror.staticClass("D").typeSignature.member(TermName("y"))) + test(c.mirror.staticClass("D").typeSignature.member(TermName("z"))) + test(c.mirror.staticClass("D").typeSignature.member(TermName("t"))) + test(c.mirror.staticClass("D").typeSignature.member(TypeName("W"))) + test(c.mirror.staticClass("D").typeSignature.member(TypeName("C"))) + test(c.mirror.staticClass("D").typeSignature.member(TermName("O"))) + + q"..${messages.map(msg => q"println($msg)")}" + } + + def foo: Any = macro impl +} \ No newline at end of file diff --git a/test/files/run/showdecl/Test_2.scala b/test/files/run/showdecl/Test_2.scala new file mode 100644 index 0000000000..65ab2f147c --- /dev/null +++ b/test/files/run/showdecl/Test_2.scala @@ -0,0 +1,32 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} + +object Test extends App { + def test(sym: Symbol): Unit = { + println(s"autoinitialized ${sym.name}: ${showDeclaration(sym)}") + sym.typeSignature + println(s"autoinitialized ${sym.name}: ${showDeclaration(sym)}") + } + + Macros.foo + println("runtime") + test(symbolOf[D]) + test(typeOf[D].member(TermName("x"))) + test(typeOf[D].member(TermName("y"))) + test(typeOf[D].member(TermName("z"))) + test(typeOf[D].member(TermName("t"))) + test(typeOf[D].member(TypeName("W"))) + test(typeOf[D].member(TypeName("C"))) + test(typeOf[D].member(TermName("O"))) +} + +class C +class D extends C { + val x = 2 + lazy val y = 3 + var z = 4 + def t[T <: Int](x: D)(y: x.W) = 5 + type W = String + class C extends D + object O extends C +} -- cgit v1.2.3