-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Conversation
What do you think about reversing the flag, |
ctx0.setSettings(summary.sstate) | ||
|
||
val ctx = | ||
if (shouldAddDocContext(ctx0)) ctx0.setProperty(ContextDoc, new ContextDocstrings) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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] { |
There was a problem hiding this comment.
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] = { |
There was a problem hiding this comment.
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 = { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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] = { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move to tasty package?
@OlivierBlanvillain I've tried switching
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. |
18b0230
to
13476fd
Compare
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`.
import java.nio.charset.Charset | ||
|
||
class CommentPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr])(implicit ctx: Context) { | ||
val buf = new TastyBuffer(5000) |
There was a problem hiding this comment.
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 = |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean?
There was a problem hiding this comment.
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 = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
The |
@nicolasstucki I've addressed your comments and modified the |
Maybe print the first |
There was a problem hiding this 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
test performance please |
performance test scheduled: 1 job(s) in queue, 1 running. |
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 = { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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]) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 = { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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]] { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 => |
There was a problem hiding this comment.
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)
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).
The comments are pickled when
-Ykeep-comments
is set.