Skip to content

Commit c0e6f99

Browse files
committed
Add ability to parse nested classes/traits docstrings
1 parent f1422cb commit c0e6f99

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
@@ -1849,28 +1849,30 @@ object Parsers {
18491849
/** TmplDef ::= ([`case'] `class' | `trait') ClassDef
18501850
* | [`case'] `object' ObjectDef
18511851
*/
1852-
def tmplDef(start: Int, mods: Modifiers): Tree = in.token match {
1853-
case TRAIT =>
1854-
classDef(posMods(start, addFlag(mods, Trait)))
1855-
case CLASS =>
1856-
classDef(posMods(start, mods))
1857-
case CASECLASS =>
1858-
classDef(posMods(start, mods | Case))
1859-
case OBJECT =>
1860-
objectDef(posMods(start, mods | Module))
1861-
case CASEOBJECT =>
1862-
objectDef(posMods(start, mods | Case | Module))
1863-
case _ =>
1864-
syntaxErrorOrIncomplete("expected start of definition")
1865-
EmptyTree
1852+
def tmplDef(start: Int, mods: Modifiers): Tree = {
1853+
val docstring = in.getDocString()
1854+
in.token match {
1855+
case TRAIT =>
1856+
classDef(posMods(start, addFlag(mods, Trait)), docstring)
1857+
case CLASS =>
1858+
classDef(posMods(start, mods), docstring)
1859+
case CASECLASS =>
1860+
classDef(posMods(start, mods | Case), docstring)
1861+
case OBJECT =>
1862+
objectDef(posMods(start, mods | Module))
1863+
case CASEOBJECT =>
1864+
objectDef(posMods(start, mods | Case | Module))
1865+
case _ =>
1866+
syntaxErrorOrIncomplete("expected start of definition")
1867+
EmptyTree
1868+
}
18661869
}
18671870

18681871
/** ClassDef ::= Id [ClsTypeParamClause]
18691872
* [ConstrMods] ClsParamClauses TemplateOpt
18701873
*/
1871-
def classDef(mods: Modifiers): TypeDef = atPos(tokenRange) {
1874+
def classDef(mods: Modifiers, docstring: Option[String]): TypeDef = atPos(tokenRange) {
18721875
val name = ident().toTypeName
1873-
val docstring = in.getDocString()
18741876
val constr = atPos(in.offset) {
18751877
val tparams = typeParamClauseOpt(ParamOwner.Class)
18761878
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)