Skip to content

Commit 1dce677

Browse files
authored
Merge pull request #4461 from dotty-staging/topic/tasty-doc
Add `Comments` section in TASTY
2 parents e88a6a5 + 3f15ee0 commit 1dce677

19 files changed

+292
-19
lines changed

compiler/src/dotty/tools/dotc/Driver.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package dotty.tools.dotc
22

33
import dotty.tools.FatalError
44
import config.CompilerCommand
5+
import core.Comments.{ContextDoc, ContextDocstrings}
56
import core.Contexts.{Context, ContextBase}
7+
import core.Mode
68
import util.DotClass
79
import reporting._
810
import scala.util.control.NonFatal
@@ -44,6 +46,11 @@ class Driver extends DotClass {
4446
val ctx = rootCtx.fresh
4547
val summary = CompilerCommand.distill(args)(ctx)
4648
ctx.setSettings(summary.sstate)
49+
50+
if (!ctx.settings.YdropComments.value(ctx) || ctx.mode.is(Mode.ReadComments)) {
51+
ctx.setProperty(ContextDoc, new ContextDocstrings)
52+
}
53+
4754
val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)(ctx)
4855
(fileNames, ctx)
4956
}

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ class ScalaSettings extends Settings.SettingGroup {
116116
val YshowPrintErrors = BooleanSetting("-Yshow-print-errors", "don't suppress exceptions thrown during tree printing.")
117117
val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler")
118118
val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.")
119-
val YkeepComments = BooleanSetting("-Ykeep-comments", "Keep comments when scanning source files.")
119+
val YdropComments = BooleanSetting("-Ydrop-comments", "Drop comments when scanning source files.")
120120
val YcookComments = BooleanSetting("-Ycook-comments", "Cook the comments (type check `@usecase`, etc.)")
121121
val YforceSbtPhases = BooleanSetting("-Yforce-sbt-phases", "Run the phases used by sbt for incremental compilation (ExtractDependencies and ExtractAPI) even if the compiler is ran outside of sbt, for debugging.")
122122
val YdumpSbtInc = BooleanSetting("-Ydump-sbt-inc", "For every compiled foo.scala, output the API representation and dependencies used for sbt incremental compilation in foo.inc, implies -Yforce-sbt-phases.")

compiler/src/dotty/tools/dotc/core/Comments.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ object Comments {
9999
}
100100

101101
object Comment {
102-
def apply(pos: Position, raw: String, expanded: Boolean = false, usc: List[UseCase] = Nil)(implicit ctx: Context): Comment =
102+
def apply(pos: Position, raw: String, expanded: Boolean = false, usc: List[UseCase] = Nil): Comment =
103103
new Comment(pos, raw) {
104104
val isExpanded = expanded
105105
val usecases = usc

compiler/src/dotty/tools/dotc/core/Mode.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,8 @@ object Mode {
9393

9494
/** We are in the IDE */
9595
val Interactive = newMode(20, "Interactive")
96+
97+
/** Read comments from definitions when unpickling from TASTY */
98+
val ReadComments = newMode(21, "ReadComments")
99+
96100
}

compiler/src/dotty/tools/dotc/core/quoted/TastyUnpickler.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ import dotty.tools.dotc.core.tasty.TastyUnpickler.NameTable
55

66
object TastyUnpickler {
77
class QuotedTreeSectionUnpickler(posUnpickler: Option[PositionUnpickler], splices: Seq[Any])
8-
extends DottyUnpickler.TreeSectionUnpickler(posUnpickler) {
8+
extends DottyUnpickler.TreeSectionUnpickler(posUnpickler, None) {
99
override def unpickle(reader: TastyReader, nameAtRef: NameTable) =
10-
new TreeUnpickler(reader, nameAtRef, posUnpickler, splices)
10+
new TreeUnpickler(reader, nameAtRef, posUnpickler, None, splices)
1111
}
1212
}
1313

14-
/** A class for unpickling quoted Tasty trees and symbols.
14+
/** A class for unpickling quoted Tasty trees and symbols. Comments are never unpickled.
1515
* @param bytes the bytearray containing the Tasty file from which we unpickle
1616
* @param splices splices that will fill the holes in the quote
1717
*/
1818
class TastyUnpickler(bytes: Array[Byte], splices: Seq[Any]) extends DottyUnpickler(bytes) {
1919
import DottyUnpickler._
2020
import TastyUnpickler._
2121

22-
protected override def treeSectionUnpickler(posUnpicklerOpt: Option[PositionUnpickler]): TreeSectionUnpickler =
22+
protected override def treeSectionUnpickler(posUnpicklerOpt: Option[PositionUnpickler], commentUnpicklerOpt: Option[CommentUnpickler]): TreeSectionUnpickler =
2323
new QuotedTreeSectionUnpickler(posUnpicklerOpt, splices)
2424
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package dotty.tools.dotc.core.tasty
2+
3+
import dotty.tools.dotc.ast.tpd
4+
import dotty.tools.dotc.core.Comments.{Comment, CommentsContext, ContextDocstrings}
5+
import dotty.tools.dotc.core.Contexts.Context
6+
import dotty.tools.dotc.core.tasty.TastyBuffer.Addr
7+
8+
import java.nio.charset.Charset
9+
10+
class CommentPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr])(implicit ctx: Context) {
11+
private[this] val buf = new TastyBuffer(5000)
12+
pickler.newSection("Comments", buf)
13+
14+
def pickleComment(root: tpd.Tree): Unit = {
15+
assert(ctx.docCtx.isDefined, "Trying to pickle comments, but there's no `docCtx`.")
16+
new Traverser(ctx.docCtx.get).traverse(root)
17+
}
18+
19+
def pickleComment(addrOfTree: Option[Addr], comment: Option[Comment]): Unit = (addrOfTree, comment) match {
20+
case (Some(addr), Some(cmt)) =>
21+
val bytes = cmt.raw.getBytes(Charset.forName("UTF-8"))
22+
val length = bytes.length
23+
buf.writeAddr(addr)
24+
buf.writeNat(length)
25+
buf.writeBytes(bytes, length)
26+
buf.writeByte(if (cmt.isExpanded) 1 else 0)
27+
case other =>
28+
()
29+
}
30+
31+
private class Traverser(docCtx: ContextDocstrings) extends tpd.TreeTraverser {
32+
override def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit =
33+
tree match {
34+
case md: tpd.MemberDef =>
35+
val comment = docCtx.docstring(md.symbol)
36+
pickleComment(addrOfTree(md), comment)
37+
traverseChildren(md)
38+
case _ =>
39+
traverseChildren(tree)
40+
}
41+
}
42+
43+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package dotty.tools.dotc.core.tasty
2+
3+
import dotty.tools.dotc.core.Comments.Comment
4+
import dotty.tools.dotc.core.Symbols.Symbol
5+
import dotty.tools.dotc.core.tasty.TastyBuffer.Addr
6+
import dotty.tools.dotc.util.Positions
7+
8+
import scala.collection.mutable.HashMap
9+
10+
import java.nio.charset.Charset
11+
12+
class CommentUnpickler(reader: TastyReader) {
13+
import reader._
14+
15+
private[tasty] lazy val comments = {
16+
val comments = new HashMap[Addr, Comment]
17+
while (!isAtEnd) {
18+
val addr = readAddr()
19+
val length = readNat()
20+
if (length > 0) {
21+
val bytes = readBytes(length)
22+
val expanded = readByte() == 1
23+
val rawComment = new String(bytes, Charset.forName("UTF-8"))
24+
comments(addr) = Comment(Positions.NoPosition, rawComment, expanded = expanded)
25+
}
26+
}
27+
comments.toMap
28+
}
29+
30+
def commentAt(addr: Addr): Option[Comment] =
31+
comments.get(addr)
32+
33+
}

compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@ object DottyUnpickler {
1717
/** Exception thrown if classfile is corrupted */
1818
class BadSignature(msg: String) extends RuntimeException(msg)
1919

20-
class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler])
20+
class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler], commentUnpickler: Option[CommentUnpickler])
2121
extends SectionUnpickler[TreeUnpickler]("ASTs") {
2222
def unpickle(reader: TastyReader, nameAtRef: NameTable) =
23-
new TreeUnpickler(reader, nameAtRef, posUnpickler, Seq.empty)
23+
new TreeUnpickler(reader, nameAtRef, posUnpickler, commentUnpickler, Seq.empty)
2424
}
2525

2626
class PositionsSectionUnpickler extends SectionUnpickler[PositionUnpickler]("Positions") {
2727
def unpickle(reader: TastyReader, nameAtRef: NameTable) =
2828
new PositionUnpickler(reader)
2929
}
30+
31+
class CommentsSectionUnpickler extends SectionUnpickler[CommentUnpickler]("Comments") {
32+
def unpickle(reader: TastyReader, nameAtRef: NameTable): CommentUnpickler =
33+
new CommentUnpickler(reader)
34+
}
3035
}
3136

3237
/** A class for unpickling Tasty trees and symbols.
@@ -38,7 +43,8 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded with t
3843

3944
val unpickler = new TastyUnpickler(bytes)
4045
private val posUnpicklerOpt = unpickler.unpickle(new PositionsSectionUnpickler)
41-
private val treeUnpickler = unpickler.unpickle(treeSectionUnpickler(posUnpicklerOpt)).get
46+
private val commentUnpicklerOpt = unpickler.unpickle(new CommentsSectionUnpickler)
47+
private val treeUnpickler = unpickler.unpickle(treeSectionUnpickler(posUnpicklerOpt, commentUnpicklerOpt)).get
4248

4349
/** Enter all toplevel classes and objects into their scopes
4450
* @param roots a set of SymDenotations that should be overwritten by unpickling
@@ -52,8 +58,8 @@ class DottyUnpickler(bytes: Array[Byte]) extends ClassfileParser.Embedded with t
5258
def unpickleTypeTree()(implicit ctx: Context): Tree =
5359
treeUnpickler.unpickleTypeTree()
5460

55-
protected def treeSectionUnpickler(posUnpicklerOpt: Option[PositionUnpickler]): TreeSectionUnpickler = {
56-
new TreeSectionUnpickler(posUnpicklerOpt)
61+
protected def treeSectionUnpickler(posUnpicklerOpt: Option[PositionUnpickler], commentUnpicklerOpt: Option[CommentUnpickler]): TreeSectionUnpickler = {
62+
new TreeSectionUnpickler(posUnpicklerOpt, commentUnpicklerOpt)
5763
}
5864

5965
protected def computeTrees(implicit ctx: Context) = treeUnpickler.unpickle()

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@ Standard Section: "Positions" Assoc*
222222
// same offset in the previously recorded node (or 0 for the first recorded node)
223223
Delta = Int // Difference between consecutive offsets,
224224
225+
Standard Section: "Comments" Comment*
226+
227+
Comment = Length Bytes Byte // Raw comment's bytes encoded as UTF-8, plus a byte indicating
228+
// whether the comment is expanded (1) or not expanded (0)
229+
230+
225231
**************************************************************************************/
226232

227233
object TastyFormat {

compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) {
3434
println("Trees:")
3535
unpickle(new TreeSectionUnpickler)
3636
unpickle(new PositionSectionUnpickler)
37+
unpickle(new CommentSectionUnpickler)
3738
}
3839

3940
class TreeSectionUnpickler extends SectionUnpickler[Unit]("ASTs") {
@@ -120,6 +121,19 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) {
120121
}
121122
}
122123

124+
class CommentSectionUnpickler extends SectionUnpickler[Unit]("Comments") {
125+
def unpickle(reader: TastyReader, tastyName: NameTable): Unit = {
126+
print(s" ${reader.endAddr.index - reader.currentAddr.index}")
127+
val comments = new CommentUnpickler(reader).comments
128+
println(s" comment bytes:")
129+
val sorted = comments.toSeq.sortBy(_._1.index)
130+
for ((addr, cmt) <- sorted) {
131+
print(treeColor("%10d".format(addr.index)))
132+
println(s": ${cmt.raw} (expanded = ${cmt.isExpanded})")
133+
}
134+
}
135+
}
136+
123137
private def nameColor(str: String): String = Magenta(str).show
124138
private def treeColor(str: String): String = Yellow(str).show
125139
private def lengthColor(str: String): String = Cyan(str).show

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotc
33
package core
44
package tasty
55

6+
import Comments.CommentsContext
67
import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._
78
import StdNames._, Denotations._, Flags._, Constants._, Annotations._
89
import NameKinds._
@@ -25,13 +26,15 @@ import scala.quoted.Types.TreeType
2526
import scala.quoted.Exprs.TastyTreeExpr
2627

2728
/** Unpickler for typed trees
28-
* @param reader the reader from which to unpickle
29-
* @param tastyName the nametable
30-
* @param posUNpicklerOpt the unpickler for positions, if it exists
29+
* @param reader the reader from which to unpickle
30+
* @param posUnpicklerOpt the unpickler for positions, if it exists
31+
* @param commentUnpicklerOpt the unpickler for comments, if it exists
32+
* @param splices
3133
*/
3234
class TreeUnpickler(reader: TastyReader,
3335
nameAtRef: NameRef => TermName,
3436
posUnpicklerOpt: Option[PositionUnpickler],
37+
commentUnpicklerOpt: Option[CommentUnpickler],
3538
splices: Seq[Any]) {
3639
import TastyFormat._
3740
import TreeUnpickler._
@@ -827,6 +830,16 @@ class TreeUnpickler(reader: TastyReader,
827830
// Child annotations for local classes and enum values are not pickled, so
828831
// need to be re-established here.
829832
sym.registerIfChild(late = true)
833+
834+
if (ctx.mode.is(Mode.ReadComments)) {
835+
assert(ctx.docCtx.isDefined, "Mode is `ReadComments`, but no `docCtx` is set.")
836+
commentUnpicklerOpt.foreach { commentUnpickler =>
837+
val comment = commentUnpickler.commentAt(start)
838+
ctx.docCtx.get.addDocstring(tree.symbol, comment)
839+
tree.setComment(comment)
840+
}
841+
}
842+
830843
tree
831844
}
832845

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ object Scanners {
172172
}
173173

174174
class Scanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends ScannerCommon(source)(ctx) {
175-
val keepComments = ctx.settings.YkeepComments.value
175+
val keepComments = !ctx.settings.YdropComments.value
176176

177177
/** All doc comments kept by their end position in a `Map` */
178178
private[this] var docstringMap: SortedMap[Int, Comment] = SortedMap.empty

compiler/src/dotty/tools/dotc/transform/Pickler.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ class Pickler extends Phase {
6060
if (tree.pos.exists)
6161
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil)
6262

63+
if (!ctx.settings.YdropComments.value)
64+
new CommentPickler(pickler, treePkl.buf.addrOfTree).pickleComment(tree)
65+
6366
// other pickle sections go here.
6467
val pickled = pickler.assembleParts()
6568
unit.pickled += (cls -> pickled)
@@ -84,6 +87,7 @@ class Pickler extends Phase {
8487
.setPeriod(Period(ctx.runId + 1, FirstPhaseId))
8588
.setReporter(new ThrowingReporter(ctx.reporter))
8689
.addMode(Mode.ReadPositions)
90+
.addMode(Mode.ReadComments)
8791
.addMode(Mode.PrintShowExceptions))
8892
result
8993
}

compiler/test/dotty/tools/DottyTest.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dotty
22
package tools
33

44
import dotc.core._
5+
import dotc.core.Comments.{ContextDoc, ContextDocstrings}
56
import dotc.core.Contexts._
67
import dotc.core.Symbols._
78
import dotc.core.Flags._
@@ -39,6 +40,7 @@ trait DottyTest extends ContextEscapeDetection {
3940
protected def initializeCtx(fc: FreshContext): Unit = {
4041
fc.setSetting(fc.settings.encoding, "UTF8")
4142
fc.setSetting(fc.settings.classpath, Jars.dottyLib)
43+
fc.setProperty(ContextDoc, new ContextDocstrings)
4244
}
4345

4446
private def compilerWithChecker(phase: String)(assertion: (tpd.Tree, Context) => Unit) = new Compiler {

0 commit comments

Comments
 (0)