diff options
-rw-r--r-- | src/dotty/tools/dotc/parsing/Parsers.scala | 34 | ||||
-rw-r--r-- | src/dotty/tools/dotc/parsing/Scanners.scala | 29 | ||||
-rw-r--r-- | test/test/DottyDocTests.scala | 48 |
3 files changed, 85 insertions, 26 deletions
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index ad50bf476..3aace28cf 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1888,28 +1888,30 @@ object Parsers { /** TmplDef ::= ([`case'] `class' | `trait') ClassDef * | [`case'] `object' ObjectDef */ - def tmplDef(start: Int, mods: Modifiers): Tree = in.token match { - case TRAIT => - classDef(posMods(start, addFlag(mods, Trait))) - case CLASS => - classDef(posMods(start, mods)) - case CASECLASS => - classDef(posMods(start, mods | Case)) - case OBJECT => - objectDef(posMods(start, mods | Module)) - case CASEOBJECT => - objectDef(posMods(start, mods | Case | Module)) - case _ => - syntaxErrorOrIncomplete("expected start of definition") - EmptyTree + def tmplDef(start: Int, mods: Modifiers): Tree = { + val docstring = in.getDocString() + in.token match { + case TRAIT => + classDef(posMods(start, addFlag(mods, Trait)), docstring) + case CLASS => + classDef(posMods(start, mods), docstring) + case CASECLASS => + classDef(posMods(start, mods | Case), docstring) + case OBJECT => + objectDef(posMods(start, mods | Module)) + case CASEOBJECT => + objectDef(posMods(start, mods | Case | Module)) + case _ => + syntaxErrorOrIncomplete("expected start of definition") + EmptyTree + } } /** ClassDef ::= Id [ClsTypeParamClause] * [ConstrMods] ClsParamClauses TemplateOpt */ - def classDef(mods: Modifiers): TypeDef = atPos(tokenRange) { + def classDef(mods: Modifiers, docstring: Option[String]): TypeDef = atPos(tokenRange) { val name = ident().toTypeName - val docstring = in.getDocString() val constr = atPos(in.offset) { val tparams = typeParamClauseOpt(ParamOwner.Class) val cmods = constrModsOpt() diff --git a/src/dotty/tools/dotc/parsing/Scanners.scala b/src/dotty/tools/dotc/parsing/Scanners.scala index 438c54398..60042e5a6 100644 --- a/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/src/dotty/tools/dotc/parsing/Scanners.scala @@ -185,12 +185,23 @@ object Scanners { /** The currently closest docstring, replaced every time a new docstring is * encountered */ - val closestDocString: mutable.Queue[Comment] = mutable.Queue.empty + var closestDocString: List[mutable.Queue[Comment]] = mutable.Queue.empty[Comment] :: Nil + + /** Adds level of nesting to docstrings */ + def enterBlock(): Unit = + closestDocString = mutable.Queue.empty[Comment] :: closestDocString + + /** Removes level of nesting for docstrings */ + def exitBlock(): Unit = closestDocString = closestDocString match { + case x :: Nil => mutable.Queue.empty[Comment] :: Nil + case _ => closestDocString.tail + } /** Returns `closestDocString`'s raw string and sets it to `None` */ - def getDocString(): Option[String] = - if (closestDocString.isEmpty) None - else Some(closestDocString.dequeue.chrs) + def getDocString(): Option[String] = closestDocString match { + case x :: _ if !x.isEmpty => Some(x.dequeue.chrs) + case _ => None + } /** A buffer for comments */ val commentBuf = new StringBuilder @@ -497,13 +508,13 @@ object Scanners { case ',' => nextChar(); token = COMMA case '(' => - nextChar(); token = LPAREN + enterBlock(); nextChar(); token = LPAREN case '{' => - nextChar(); token = LBRACE + enterBlock(); nextChar(); token = LBRACE case ')' => - nextChar(); token = RPAREN + exitBlock(); nextChar(); token = RPAREN case '}' => - nextChar(); token = RBRACE + exitBlock(); nextChar(); token = RBRACE case '[' => nextChar(); token = LBRACKET case ']' => @@ -569,7 +580,7 @@ object Scanners { val comment = Comment(pos, flushBuf(commentBuf)) if (comment.isDocComment) - closestDocString.enqueue(comment) + closestDocString.head.enqueue(comment) if (keepComments) revComments = comment :: revComments diff --git a/test/test/DottyDocTests.scala b/test/test/DottyDocTests.scala index df53c2ae9..af3336cb7 100644 --- a/test/test/DottyDocTests.scala +++ b/test/test/DottyDocTests.scala @@ -60,7 +60,9 @@ object DottyDocTests extends DottyTest { SingleCaseClassWithoutPackage, SingleTraitWihoutPackage, MultipleTraitsWithoutPackage, - MultipleMixedEntitiesWithPackage + MultipleMixedEntitiesWithPackage, + NestedClass, + NestedClassThenOuter ) def main(args: Array[String]): Unit = { @@ -185,3 +187,47 @@ case object MultipleMixedEntitiesWithPackage extends DottyDocTest { } } } + +case object NestedClass extends DottyDocTest { + override val source = + """ + |/** Outer docstring */ + |class Outer { + | /** Inner docstring */ + | class Inner(val x: Int) + |} + """.stripMargin + + override def assertion = { + case PackageDef(_, Seq(outer @ TypeDef(_, tpl @ Template(_,_,_,_)))) => + checkDocString(outer.rawComment, "/** Outer docstring */") + tpl.body match { + case (inner @ TypeDef(_,_)) :: _ => checkDocString(inner.rawComment, "/** Inner docstring */") + case _ => assert(false, "Couldn't find inner class") + } + } +} + +case object NestedClassThenOuter extends DottyDocTest { + override val source = + """ + |/** Outer1 docstring */ + |class Outer1 { + | /** Inner docstring */ + | class Inner(val x: Int) + |} + | + |/** Outer2 docstring */ + |class Outer2 + """.stripMargin + + override def assertion = { + case PackageDef(_, Seq(o1 @ TypeDef(_, tpl @ Template(_,_,_,_)), o2 @ TypeDef(_,_))) => + checkDocString(o1.rawComment, "/** Outer1 docstring */") + checkDocString(o2.rawComment, "/** Outer2 docstring */") + tpl.body match { + case (inner @ TypeDef(_,_)) :: _ => checkDocString(inner.rawComment, "/** Inner docstring */") + case _ => assert(false, "Couldn't find inner class") + } + } +} |