Skip to content

Commit da1f2a0

Browse files
committed
Add ability to parse nested classes/traits docstrings
1 parent aa54e82 commit da1f2a0

File tree

3 files changed

+85
-26
lines changed

3 files changed

+85
-26
lines changed

src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,28 +1886,30 @@ object Parsers {
18861886
/** TmplDef ::= ([`case'] `class' | `trait') ClassDef
18871887
* | [`case'] `object' ObjectDef
18881888
*/
1889-
def tmplDef(start: Int, mods: Modifiers): Tree = in.token match {
1890-
case TRAIT =>
1891-
classDef(posMods(start, addFlag(mods, Trait)))
1892-
case CLASS =>
1893-
classDef(posMods(start, mods))
1894-
case CASECLASS =>
1895-
classDef(posMods(start, mods | Case))
1896-
case OBJECT =>
1897-
objectDef(posMods(start, mods | Module))
1898-
case CASEOBJECT =>
1899-
objectDef(posMods(start, mods | Case | Module))
1900-
case _ =>
1901-
syntaxErrorOrIncomplete("expected start of definition")
1902-
EmptyTree
1889+
def tmplDef(start: Int, mods: Modifiers): Tree = {
1890+
val docstring = in.getDocString()
1891+
in.token match {
1892+
case TRAIT =>
1893+
classDef(posMods(start, addFlag(mods, Trait)), docstring)
1894+
case CLASS =>
1895+
classDef(posMods(start, mods), docstring)
1896+
case CASECLASS =>
1897+
classDef(posMods(start, mods | Case), docstring)
1898+
case OBJECT =>
1899+
objectDef(posMods(start, mods | Module))
1900+
case CASEOBJECT =>
1901+
objectDef(posMods(start, mods | Case | Module))
1902+
case _ =>
1903+
syntaxErrorOrIncomplete("expected start of definition")
1904+
EmptyTree
1905+
}
19031906
}
19041907

19051908
/** ClassDef ::= Id [ClsTypeParamClause]
19061909
* [ConstrMods] ClsParamClauses TemplateOpt
19071910
*/
1908-
def classDef(mods: Modifiers): TypeDef = atPos(tokenRange) {
1911+
def classDef(mods: Modifiers, docstring: Option[String]): TypeDef = atPos(tokenRange) {
19091912
val name = ident().toTypeName
1910-
val docstring = in.getDocString()
19111913
val constr = atPos(in.offset) {
19121914
val tparams = typeParamClauseOpt(ParamOwner.Class)
19131915
val cmods = constrModsOpt()

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

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,23 @@ object Scanners {
185185
/** The currently closest docstring, replaced every time a new docstring is
186186
* encountered
187187
*/
188-
val closestDocString: mutable.Queue[Comment] = mutable.Queue.empty
188+
var closestDocString: List[mutable.Queue[Comment]] = mutable.Queue.empty[Comment] :: Nil
189+
190+
/** Adds level of nesting to docstrings */
191+
def enterBlock(): Unit =
192+
closestDocString = mutable.Queue.empty[Comment] :: closestDocString
193+
194+
/** Removes level of nesting for docstrings */
195+
def exitBlock(): Unit = closestDocString = closestDocString match {
196+
case x :: Nil => mutable.Queue.empty[Comment] :: Nil
197+
case _ => closestDocString.tail
198+
}
189199

190200
/** Returns `closestDocString`'s raw string and sets it to `None` */
191-
def getDocString(): Option[String] =
192-
if (closestDocString.isEmpty) None
193-
else Some(closestDocString.dequeue.chrs)
201+
def getDocString(): Option[String] = closestDocString match {
202+
case x :: _ if !x.isEmpty => Some(x.dequeue.chrs)
203+
case _ => None
204+
}
194205

195206
/** A buffer for comments */
196207
val commentBuf = new StringBuilder
@@ -497,13 +508,13 @@ object Scanners {
497508
case ',' =>
498509
nextChar(); token = COMMA
499510
case '(' =>
500-
nextChar(); token = LPAREN
511+
enterBlock(); nextChar(); token = LPAREN
501512
case '{' =>
502-
nextChar(); token = LBRACE
513+
enterBlock(); nextChar(); token = LBRACE
503514
case ')' =>
504-
nextChar(); token = RPAREN
515+
exitBlock(); nextChar(); token = RPAREN
505516
case '}' =>
506-
nextChar(); token = RBRACE
517+
exitBlock(); nextChar(); token = RBRACE
507518
case '[' =>
508519
nextChar(); token = LBRACKET
509520
case ']' =>
@@ -569,7 +580,7 @@ object Scanners {
569580
val comment = Comment(pos, flushBuf(commentBuf))
570581

571582
if (comment.isDocComment)
572-
closestDocString.enqueue(comment)
583+
closestDocString.head.enqueue(comment)
573584

574585
if (keepComments)
575586
revComments = comment :: revComments

test/test/DottyDocTests.scala

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ object DottyDocTests extends DottyTest {
6060
SingleCaseClassWithoutPackage,
6161
SingleTraitWihoutPackage,
6262
MultipleTraitsWithoutPackage,
63-
MultipleMixedEntitiesWithPackage
63+
MultipleMixedEntitiesWithPackage,
64+
NestedClass,
65+
NestedClassThenOuter
6466
)
6567

6668
def main(args: Array[String]): Unit = {
@@ -185,3 +187,47 @@ case object MultipleMixedEntitiesWithPackage extends DottyDocTest {
185187
}
186188
}
187189
}
190+
191+
case object NestedClass extends DottyDocTest {
192+
override val source =
193+
"""
194+
|/** Outer docstring */
195+
|class Outer {
196+
| /** Inner docstring */
197+
| class Inner(val x: Int)
198+
|}
199+
""".stripMargin
200+
201+
override def assertion = {
202+
case PackageDef(_, Seq(outer @ TypeDef(_, tpl @ Template(_,_,_,_)))) =>
203+
checkDocString(outer.rawComment, "/** Outer docstring */")
204+
tpl.body match {
205+
case (inner @ TypeDef(_,_)) :: _ => checkDocString(inner.rawComment, "/** Inner docstring */")
206+
case _ => assert(false, "Couldn't find inner class")
207+
}
208+
}
209+
}
210+
211+
case object NestedClassThenOuter extends DottyDocTest {
212+
override val source =
213+
"""
214+
|/** Outer1 docstring */
215+
|class Outer1 {
216+
| /** Inner docstring */
217+
| class Inner(val x: Int)
218+
|}
219+
|
220+
|/** Outer2 docstring */
221+
|class Outer2
222+
""".stripMargin
223+
224+
override def assertion = {
225+
case PackageDef(_, Seq(o1 @ TypeDef(_, tpl @ Template(_,_,_,_)), o2 @ TypeDef(_,_))) =>
226+
checkDocString(o1.rawComment, "/** Outer1 docstring */")
227+
checkDocString(o2.rawComment, "/** Outer2 docstring */")
228+
tpl.body match {
229+
case (inner @ TypeDef(_,_)) :: _ => checkDocString(inner.rawComment, "/** Inner docstring */")
230+
case _ => assert(false, "Couldn't find inner class")
231+
}
232+
}
233+
}

0 commit comments

Comments
 (0)