Skip to content

Add Comments section in TASTY #4461

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 26, 2018
Merged

Conversation

Duhemm
Copy link
Contributor

@Duhemm Duhemm commented May 4, 2018

The comments are pickled when -Ykeep-comments is set.

@OlivierBlanvillain
Copy link
Contributor

OlivierBlanvillain commented May 4, 2018

What do you think about reversing the flag, -Ydrop-comments, (default false)? Python has great success with carrying comments everywhere!

ctx0.setSettings(summary.sstate)

val ctx =
if (shouldAddDocContext(ctx0)) ctx0.setProperty(ContextDoc, new ContextDocstrings)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need ctx0, Context.setProperty mutates the context

import org.junit.Test
import org.junit.Assert.{assertEquals, assertFalse, fail}

class CommentPicklingTest extends ParallelTesting {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this extends ParallelTesting?

maxDepth: Int = Int.MaxValue): Seq[Path] = {
val out = collection.mutable.ListBuffer.empty[Path]
val matcher = FileSystems.getDefault.getPathMatcher(pattern)
val visitor = new FileVisitor[Path] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like?

val paths = Files.walk(base)
try paths.filter(matcher.matches).iterator().asScala.toList
finally paths.close()


private def getAll(base: Path,
pattern: String,
maxDepth: Int = Int.MaxValue): Seq[Path] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maxDepth is not used. Remove?

compileAndCheckComment(sources, "buzz".toTermName, Some("/** foo */"))
}

private def compileAndCheckComment(sources: Seq[String], treeName: Name, expectedComment: Option[String]): Unit = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use List instead of Seq across the file. I don't think you gain anything by using Seq

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we optimise the API for the most common use case where you want to check for the presence of a comment? Option[String] => String.

Also maybe add an overload for one source

}
}

private def findTreeNamed(name: Name)(trees: Seq[tpd.Tree], ctx: Context): Option[tpd.NameTree] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have find on Tree in tpd.TreeOps

@@ -0,0 +1,178 @@
package dotty.tools.dotc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to tasty package?

@Duhemm Duhemm force-pushed the topic/tasty-doc branch from 378b9f5 to 720ca20 Compare May 23, 2018 08:10
@Duhemm
Copy link
Contributor Author

Duhemm commented May 23, 2018

@OlivierBlanvillain I've tried switching -Ykeep-comments to -Ydrop-comments, but unfortunately this doesn't work with -Ycheck at the moment. There are several reasons for that:

I'd prefer to leave the switching of the flag for a later PR, when it's clearer to me what needs to be fixed in order to keep the comments by default.

@Duhemm Duhemm force-pushed the topic/tasty-doc branch 2 times, most recently from 18b0230 to 13476fd Compare May 24, 2018 05:57
@Duhemm Duhemm force-pushed the topic/tasty-doc branch from 13476fd to 7a4fbcb Compare June 6, 2018 04:39
Duhemm added 5 commits June 11, 2018 10:51
The comments are pickled when `-Ykeep-comments` is set.
This commit removes `-Ykeep-comments` and adds a new `-Ydrop-comments`
flag. `-Ydrop-comments` is used to prevent the parser from keeping the
comments, and defaults to `false`.
@Duhemm Duhemm force-pushed the topic/tasty-doc branch from 7a4fbcb to deb9dc8 Compare June 11, 2018 08:51
@Duhemm Duhemm requested a review from nicolasstucki June 11, 2018 13:55
@nicolasstucki nicolasstucki self-assigned this Jun 11, 2018
@Duhemm Duhemm mentioned this pull request Jun 12, 2018
import java.nio.charset.Charset

class CommentPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr])(implicit ctx: Context) {
val buf = new TastyBuffer(5000)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private[this]

@@ -48,6 +48,10 @@ class TastyBuffer(initialSize: Int) {

// -- Output routines --------------------------------------------

/** Write a boolean value. */
def writeBoolean(b: Boolean): Unit =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would write the byte explicitly pickleComment and read the byte in CommentUnpickler. This abstraction is only used once and hides the underlying representation. It would make it too easy to write two calls to writeBoolean instead of one to writeByte (with bitset representation).

buf.writeAddr(addr)
buf.writeNat(length)
buf.writeBytes(bytes, length)
buf.writeBoolean(cmt.isExpanded)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be missing from the documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got it now

@@ -41,6 +41,10 @@ class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int =
def subReader(start: Addr, end: Addr): TastyReader =
new TastyReader(bytes, index(start), index(end), base)

/** Read a boolean value. */
def readBoolean(): Boolean =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

@nicolasstucki
Copy link
Contributor

The TastyPrinter should print something for this section as is done for positions. You can test the printer with dotc -print-tasty XYZ for class XYZ in your classpath. This can be done in another PR.

@Duhemm
Copy link
Contributor Author

Duhemm commented Jun 13, 2018

@nicolasstucki I've addressed your comments and modified the TastyPrinter to print the comments, too. Maybe it's too verbose to print the whole comments? I don't know how you usually use it, but it may be better to print only the first line of every comment?

@Duhemm Duhemm assigned nicolasstucki and unassigned Duhemm Jun 13, 2018
@nicolasstucki
Copy link
Contributor

Maybe print the first n characters of the string and then add ... and say on the side the actual length of the comment. n could be based on the current value of -pagewidth.

Copy link
Contributor

@nicolasstucki nicolasstucki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should improve the format of the printer later

@nicolasstucki nicolasstucki removed their assignment Jun 13, 2018
@allanrenucci
Copy link
Contributor

test performance please

@dottybot
Copy link
Member

performance test scheduled: 1 job(s) in queue, 1 running.

@dottybot
Copy link
Member

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/4461/ to see the changes.

Benchmarks is based on merging with master (78a5e09)

* to store doc comments unless `-Ydrop-comments` is set, or when TASTY is configured to
* unpickle the doc comments.
*/
protected def shouldAddDocContext(implicit ctx: Context): Boolean = {
Copy link
Contributor

@allanrenucci allanrenucci Jun 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why protected? It seems to be only used here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is likely to be used in setup or an override of setup, and everything in this class is protected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely or not, this is not currently the case. I would just inline it into setup. In the future, if there is a use case, one can always extract the wanted bits into a protected def

@@ -4,10 +4,10 @@ import dotty.tools.dotc.core.tasty._
import dotty.tools.dotc.core.tasty.TastyUnpickler.NameTable

object TastyUnpickler {
class QuotedTreeSectionUnpickler(posUnpickler: Option[PositionUnpickler], splices: Seq[Any])
extends DottyUnpickler.TreeSectionUnpickler(posUnpickler) {
class QuotedTreeSectionUnpickler(posUnpickler: Option[PositionUnpickler], commentUnpickler: Option[CommentUnpickler], splices: Seq[Any])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolasstucki Does it make sense to pickle comment for quoted trees?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not have a use case in mind. We should not add it.

@@ -820,6 +823,16 @@ class TreeUnpickler(reader: TastyReader,
// Child annotations for local classes and enum values are not pickled, so
// need to be re-established here.
sym.registerIfChild(late = true)

if (ctx.mode.is(Mode.ReadComments)) {
for { docCtx <- ctx.docCtx
Copy link
Contributor

@allanrenucci allanrenucci Jun 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the mode is ReadComments, then I suppose both docCtx and commentUnpickler should not be empty

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same goes for posUnpicklerOpt: we check if the mode is ReadPositions and whether posUnpicklerOpt is set.

The TreeUnpickler can be constructed with a certain configuration in mind (i.e. not defining commentUnpicklerOpt), and then used with a different configuration (i.e. ctx.mode.is(Mode.ReadComments)), which would lead to crashes if we just used .get on the Option.

Do we prefer crashing? Should we add a warning?

Copy link
Contributor

@allanrenucci allanrenucci Jun 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really can't tell why the PositionUnpickler is taken as an option.

I don't see a good reason to do the same. It only makes thing more complicated. I think the TreeUnpickler does not need an optional CommentUnpickler. I think we should just look at Mode.ReadComments to decide whether or not we unpickle comments. And in the case we do, we should assert that we have a docCtx.

Maybe @smarter or @nicolasstucki can explain why it was done like this for PositionUnpickler

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea, but what you said sounds reasonable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually valid for the positionUnpickler or commentUnpickler to be None if there are no Positions or Comments sections in the TASTY file from which we're unpickling.

* unpickle the doc comments.
*/
protected def shouldAddDocContext(implicit ctx: Context): Boolean = {
!ctx.settings.YdropComments.value || ctx.mode.is(Mode.ReadComments)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have both a new mode and a compiler option?
Isn't !ctx.settings.YdropComments <=> Mode.ReadComments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, ReadComments configures whether the unpickler should unpickle comments. You may want to keep comments that are in your code, but don't want to unpickle comments from your dependencies.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough

def pickleComment(root: tpd.Tree): Unit =
new Traverser().traverse(root)

def pickleComment(addrOfTree: Option[Addr], comment: Option[Comment]): Unit = (addrOfTree, comment) match {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private?

* to store doc comments unless `-Ydrop-comments` is set, or when TASTY is configured to
* unpickle the doc comments.
*/
protected def shouldAddDocContext(implicit ctx: Context): Boolean = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely or not, this is not currently the case. I would just inline it into setup. In the future, if there is a use case, one can always extract the wanted bits into a protected def

* unpickle the doc comments.
*/
protected def shouldAddDocContext(implicit ctx: Context): Boolean = {
!ctx.settings.YdropComments.value || ctx.mode.is(Mode.ReadComments)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough

()
}

private class Traverser extends tpd.TreeTraverser {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the traverser take the docCtx and only create the traverser in pickleComment if it exists

@@ -820,6 +823,16 @@ class TreeUnpickler(reader: TastyReader,
// Child annotations for local classes and enum values are not pickled, so
// need to be re-established here.
sym.registerIfChild(late = true)

if (ctx.mode.is(Mode.ReadComments)) {
for { docCtx <- ctx.docCtx
Copy link
Contributor

@allanrenucci allanrenucci Jun 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really can't tell why the PositionUnpickler is taken as an option.

I don't see a good reason to do the same. It only makes thing more complicated. I think the TreeUnpickler does not need an optional CommentUnpickler. I think we should just look at Mode.ReadComments to decide whether or not we unpickle comments. And in the case we do, we should assert that we have a docCtx.

Maybe @smarter or @nicolasstucki can explain why it was done like this for PositionUnpickler

}

private def findTreeNamed(name: Name)(trees: List[tpd.Tree], ctx: Context): Option[tpd.NameTree] = {
val acc = new tpd.TreeAccumulator[Option[tpd.NameTree]] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use TreeOps.find from tpd.scala?

Files.createDirectories(out)

val options = compileOptions.and("-d", out.toAbsolutePath.toString).and(sourceFiles: _*)
val driver = new Driver
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dotty.tools.package.dotc.Main?

}

private def compileAndUnpickle[T](sources: List[String])(fn: (List[tpd.Tree], Context) => T) = {
inTempDirectory { tmp =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nice to have (that shouldn't block this PR): a driver that compiles from virtual files. I think it would be fairly easy: override doCompile and call compileSources(List[SourceFile]) instead of compile(List[String]). Creating a virtual source file is straightforward: new SourceFile("<fake name>", content.toCharArray)

Duhemm added 2 commits June 19, 2018 09:04
Now that we check that the `DocContext` is set, we need to set it in the
tests (or add `-Ydiscard-comments`, but this better reflects how Dotty
is going to be used).
@allanrenucci allanrenucci merged commit 1dce677 into scala:master Jun 26, 2018
@allanrenucci allanrenucci deleted the topic/tasty-doc branch June 26, 2018 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants