From 88bf54cd851dfcfa686950553e976ca116a1346b Mon Sep 17 00:00:00 2001 From: Aleksander Boruch-Gruszecki Date: Wed, 6 Mar 2019 10:37:10 +0100 Subject: [PATCH] Add tastyreflect comment API --- .../dotc/consumetasty/TastyFromClass.scala | 8 +++++ .../dotc/tastyreflect/CommentOpsImpl.scala | 10 ++++++ .../tools/dotc/tastyreflect/CoreImpl.scala | 2 ++ .../dotc/tastyreflect/ReflectionImpl.scala | 1 + .../dotc/tastyreflect/SymbolOpsImpl.scala | 10 ++++++ library/src/scala/tasty/Reflection.scala | 1 + .../src/scala/tasty/reflect/CommentOps.scala | 19 ++++++++++++ library/src/scala/tasty/reflect/Core.scala | 3 ++ .../src/scala/tasty/reflect/SymbolOps.scala | 3 ++ .../tasty-comment-consumer.check | 7 +++++ .../tasty-comment-consumer/Foo.scala | 8 +++++ .../tasty-comment-consumer/Test.scala | 31 +++++++++++++++++++ tests/run/tasty-comments.check | 3 ++ tests/run/tasty-comments/quoted_1.scala | 19 ++++++++++++ tests/run/tasty-comments/quoted_2.scala | 18 +++++++++++ 15 files changed, 143 insertions(+) create mode 100644 compiler/src/dotty/tools/dotc/tastyreflect/CommentOpsImpl.scala create mode 100644 library/src/scala/tasty/reflect/CommentOps.scala create mode 100644 tests/run-with-compiler/tasty-comment-consumer.check create mode 100644 tests/run-with-compiler/tasty-comment-consumer/Foo.scala create mode 100644 tests/run-with-compiler/tasty-comment-consumer/Test.scala create mode 100644 tests/run/tasty-comments.check create mode 100644 tests/run/tasty-comments/quoted_1.scala create mode 100644 tests/run/tasty-comments/quoted_2.scala diff --git a/compiler/src/dotty/tools/dotc/consumetasty/TastyFromClass.scala b/compiler/src/dotty/tools/dotc/consumetasty/TastyFromClass.scala index c5e5e98d810e..afc811e54cc1 100644 --- a/compiler/src/dotty/tools/dotc/consumetasty/TastyFromClass.scala +++ b/compiler/src/dotty/tools/dotc/consumetasty/TastyFromClass.scala @@ -1,5 +1,8 @@ package dotty.tools.dotc.consumetasty +import dotty.tools.dotc.Run +import dotty.tools.dotc.core.Mode +import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.fromtasty._ @@ -17,4 +20,9 @@ class TastyFromClass(consumer: TastyConsumer) extends TASTYCompiler { override protected def backendPhases: List[List[Phase]] = List(new TastyConsumerPhase(consumer)) :: // Print all loaded classes Nil + + override def newRun(implicit ctx: Context): Run = { + reset() + new TASTYRun(this, ctx.fresh.addMode(Mode.ReadPositions).addMode(Mode.ReadComments)) + } } diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/CommentOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/CommentOpsImpl.scala new file mode 100644 index 000000000000..6c6fdebd4524 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tastyreflect/CommentOpsImpl.scala @@ -0,0 +1,10 @@ +package dotty.tools.dotc.tastyreflect + +trait CommentOpsImpl extends scala.tasty.reflect.CommentOps with CoreImpl { + + implicit def CommentDeco(com: Comment): CommentAPI = new CommentAPI { + override def raw: String = com.raw + override def expanded: Option[String] = com.expanded + override def usecases: List[(String, Option[DefDef])] = com.usecases.map { uc => (uc.code, uc.tpdCode) } + } +} diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/CoreImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/CoreImpl.scala index b9d37c1e3647..83220030ac55 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/CoreImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/CoreImpl.scala @@ -115,6 +115,8 @@ trait CoreImpl extends scala.tasty.reflect.Core { type Position = util.SourcePosition + type Comment = core.Comments.Comment + type Constant = Constants.Constant type Symbol = core.Symbols.Symbol diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionImpl.scala index 34a2a37285e4..bd689b7ee70a 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionImpl.scala @@ -8,6 +8,7 @@ class ReflectionImpl(val rootContext: Contexts.Context) with CaseDefOpsImpl with ConstantOpsImpl with ContextOpsImpl + with CommentOpsImpl with FlagsOpsImpl with IdOpsImpl with ImportSelectorOpsImpl diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala index 1e2600a3d86c..58bfb03289eb 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala @@ -28,6 +28,16 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl { def pos(implicit ctx: Context): Position = symbol.sourcePos + def comment(implicit ctx: Context): Option[Comment] = { + import dotty.tools.dotc.core.Comments.CommentsContext + val docCtx = ctx.docCtx.getOrElse { + throw new RuntimeException( + "DocCtx could not be found and comments are unavailable. This is a compiler-internal error." + ) + } + docCtx.docstring(symbol) + } + def owner(implicit ctx: Context): Symbol = symbol.owner def isLocalDummy(implicit ctx: Context): Boolean = symbol.isLocalDummy diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index 66a69b065484..d891eea6e73e 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -7,6 +7,7 @@ abstract class Reflection with CaseDefOps with ConstantOps with ContextOps + with CommentOps with FlagsOps with IdOps with ImportSelectorOps diff --git a/library/src/scala/tasty/reflect/CommentOps.scala b/library/src/scala/tasty/reflect/CommentOps.scala new file mode 100644 index 000000000000..97480f7160e6 --- /dev/null +++ b/library/src/scala/tasty/reflect/CommentOps.scala @@ -0,0 +1,19 @@ +package scala.tasty.reflect + +trait CommentOps extends Core { + + trait CommentAPI { + + /** Raw comment string */ + def raw: String + + /** Expanded comment string, if any */ + def expanded: Option[String] + + /** List of usecases and their corresponding trees, if any */ + def usecases: List[(String, Option[DefDef])] + + } + implicit def CommentDeco(com: Comment): CommentAPI + +} diff --git a/library/src/scala/tasty/reflect/Core.scala b/library/src/scala/tasty/reflect/Core.scala index c86a1e0447f6..3da2cea1a533 100644 --- a/library/src/scala/tasty/reflect/Core.scala +++ b/library/src/scala/tasty/reflect/Core.scala @@ -410,6 +410,9 @@ trait Core { /** Source position */ type Position <: AnyRef + /** Comment */ + type Comment <: AnyRef + /** Constant value represented as the constant itself */ type Constant <: AnyRef diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index 6d3b2439cbf4..d2aec1152da0 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -37,6 +37,9 @@ trait SymbolOps extends Core { /** The position of this symbol */ def pos(implicit ctx: Context): Position + /** The comment for this symbol, if any */ + def comment(implicit ctx: Context): Option[Comment] + def localContext(implicit ctx: Context): Context /** Unsafe cast as to PackageSymbol. Use IsPackageSymbol to safly check and cast to PackageSymbol */ diff --git a/tests/run-with-compiler/tasty-comment-consumer.check b/tests/run-with-compiler/tasty-comment-consumer.check new file mode 100644 index 000000000000..f95e1d4e7b15 --- /dev/null +++ b/tests/run-with-compiler/tasty-comment-consumer.check @@ -0,0 +1,7 @@ +/** Object with comment */ +/** Object with comment */ + + + +/** Val with comment */ + diff --git a/tests/run-with-compiler/tasty-comment-consumer/Foo.scala b/tests/run-with-compiler/tasty-comment-consumer/Foo.scala new file mode 100644 index 000000000000..4c0a0943fead --- /dev/null +++ b/tests/run-with-compiler/tasty-comment-consumer/Foo.scala @@ -0,0 +1,8 @@ +/** Object with comment */ +object Foo { + + /** Val with comment */ + val x = null + + val y = null // val without comment +} diff --git a/tests/run-with-compiler/tasty-comment-consumer/Test.scala b/tests/run-with-compiler/tasty-comment-consumer/Test.scala new file mode 100644 index 000000000000..ef2251fa43e9 --- /dev/null +++ b/tests/run-with-compiler/tasty-comment-consumer/Test.scala @@ -0,0 +1,31 @@ +import scala.tasty.Reflection +import scala.tasty.file._ + +object Test { + def main(args: Array[String]): Unit = { + ConsumeTasty("", List("Foo"), new CommentConsumer) + } +} + +class CommentConsumer extends TastyConsumer { + + final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { + import reflect._ + object Traverser extends TreeTraverser { + + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = tree match { + case IsDefinition(tree) => + tree.symbol.comment match { + case Some(com) => println(com.raw) + case None => println() + } + super.traverseTree(tree) + case tree => + super.traverseTree(tree) + } + + } + Traverser.traverseTree(root)(reflect.rootContext) + } + +} diff --git a/tests/run/tasty-comments.check b/tests/run/tasty-comments.check new file mode 100644 index 000000000000..152c776676a1 --- /dev/null +++ b/tests/run/tasty-comments.check @@ -0,0 +1,3 @@ +/** Object with a docstring */ +/** Val with a docstring */ + diff --git a/tests/run/tasty-comments/quoted_1.scala b/tests/run/tasty-comments/quoted_1.scala new file mode 100644 index 000000000000..b44eef17e220 --- /dev/null +++ b/tests/run/tasty-comments/quoted_1.scala @@ -0,0 +1,19 @@ +import scala.quoted._ + +import scala.tasty._ + +object Macros { + + inline def printComment[T](t: => T): Unit = + ${ impl('t) } + + def impl[T](x: Expr[T])(implicit reflect: Reflection): Expr[Unit] = { + import reflect._ + + val tree = x.unseal + tree.symbol.comment.map(_.raw) match { + case Some(str) => '{ println(${str.toExpr}) } + case None => '{ println() } + } + } +} diff --git a/tests/run/tasty-comments/quoted_2.scala b/tests/run/tasty-comments/quoted_2.scala new file mode 100644 index 000000000000..e426a5f4cd22 --- /dev/null +++ b/tests/run/tasty-comments/quoted_2.scala @@ -0,0 +1,18 @@ +import Macros._ + +object Test { + + /** Object with a docstring */ + object Obj + + /** Val with a docstring */ + val x: Null = null + + val y: Null = null // val without a docstring + + def main(args: Array[String]): Unit = { + printComment(Obj) + printComment(x) + printComment(y) + } +}