Skip to content

Commit 6fb9c64

Browse files
committed
Handle overridden symbols in doc command in REPL
1 parent 5b347bf commit 6fb9c64

File tree

2 files changed

+73
-11
lines changed

2 files changed

+73
-11
lines changed

compiler/src/dotty/tools/repl/ReplCompiler.scala

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -169,30 +169,43 @@ class ReplCompiler(val directory: AbstractFile) extends Compiler {
169169
def docOf(expr: String)(implicit state: State): Result[String] = {
170170
implicit val ctx: Context = state.context
171171

172-
def pickSymbol(symbol: Symbol): Symbol = {
173-
if (symbol.is(Module, butNot = ModuleClass)) symbol.moduleClass
174-
if (symbol.isConstructor) symbol.owner
175-
else symbol
176-
}
177-
172+
/** Extract the (possibly) documented symbol from `tree` */
178173
def extractSymbol(tree: tpd.Tree): Symbol = {
179174
tree match {
180175
case tpd.closureDef(defdef) => defdef.rhs.symbol
181176
case _ => tree.symbol
182177
}
183178
}
184179

180+
/**
181+
* Adapt `symbol` so that we get the one that holds the documentation.
182+
* Return also the symbols that `symbol` overrides`.
183+
*/
184+
def pickSymbols(symbol: Symbol): Iterator[Symbol] = {
185+
val selectedSymbol = {
186+
if (symbol.is(Module, butNot = ModuleClass)) symbol.moduleClass
187+
if (symbol.isConstructor) symbol.owner
188+
else symbol
189+
}
190+
191+
Iterator(selectedSymbol) ++ selectedSymbol.allOverriddenSymbols
192+
}
193+
185194
typeCheck(expr).map {
186195
case v @ ValDef(_, _, Block(stats, _)) if stats.nonEmpty =>
187196
val stat = stats.last.asInstanceOf[tpd.Tree]
188197
if (stat.tpe.isError) stat.tpe.show
189198
else {
190-
val symbol = pickSymbol(extractSymbol(stat))
191199
val doc =
192-
for { docCtx <- ctx.docCtx
193-
doc <- docCtx.docstrings.get(symbol) } yield doc.raw
194-
195-
doc.getOrElse(s"// No doc for ${symbol.show}")
200+
ctx.docCtx.flatMap { docCtx =>
201+
val symbols = pickSymbols(extractSymbol(stat))
202+
symbols.collectFirst {
203+
case sym if docCtx.docstrings.contains(sym) =>
204+
docCtx.docstrings(sym).raw
205+
}
206+
}
207+
208+
doc.getOrElse(s"// No doc for `${expr}`")
196209
}
197210

198211
case _ =>

compiler/test/dotty/tools/repl/DocTests.scala

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,53 @@ class DocTests extends ReplTest {
157157
assertEquals("/** doc */", storedOutput().trim)
158158
}
159159

160+
@Test def docOfOverride =
161+
fromInitialState { implicit s =>
162+
run("""abstract class A {
163+
|/** doc0 */ def foo(x: Int): Int = x + 1
164+
|/** doc1 */ def foo(x: String): String = x + "foo"
165+
|}""".stripMargin)
166+
}
167+
.andThen { implicit s =>
168+
run("""object O extends A {
169+
| override def foo(x: Int): Int = x
170+
| /** overridden doc */ override def foo(x: String): String = x
171+
|}""".stripMargin)
172+
}
173+
.andThen { implicit s =>
174+
storedOutput()
175+
run(":doc O.foo(_: Int)")
176+
assertEquals("/** doc0 */", storedOutput().trim)
177+
s
178+
}
179+
.andThen { implicit s =>
180+
run(":doc O.foo(_: String)")
181+
assertEquals("/** overridden doc */", storedOutput().trim)
182+
}
183+
184+
@Test def docOfOverrideObject =
185+
fromInitialState { implicit s =>
186+
run("""abstract class A {
187+
| abstract class Companion { /** doc0 */ def bar: Int }
188+
| /** companion */ def foo: Companion
189+
|}""".stripMargin)
190+
.andThen { implicit s =>
191+
run("""object O extends A {
192+
| override object foo extends Companion {
193+
| override def bar: Int = 0
194+
| }
195+
|}""".stripMargin)
196+
}
197+
.andThen { implicit s =>
198+
storedOutput()
199+
run(":doc O.foo")
200+
assertEquals("/** companion */", storedOutput().trim)
201+
s
202+
}
203+
.andThen { implicit s =>
204+
run(":doc O.foo.bar")
205+
assertEquals("/** doc0 */", storedOutput().trim)
206+
}
207+
}
208+
160209
}

0 commit comments

Comments
 (0)