summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala8
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala93
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/Entity.scala4
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala37
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala9
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala16
-rw-r--r--test/scaladoc/resources/doc-root/Any.scala114
-rw-r--r--test/scaladoc/resources/doc-root/AnyRef.scala131
-rw-r--r--test/scaladoc/resources/doc-root/Nothing.scala23
-rw-r--r--test/scaladoc/resources/doc-root/Null.scala17
-rw-r--r--test/scaladoc/run/SI-5780.check1
-rw-r--r--test/scaladoc/run/SI-5780.scala25
12 files changed, 419 insertions, 59 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala
index 16d859894f..ec00cace75 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala
@@ -38,6 +38,8 @@ object DiagramStats {
private[this] val dotGenTrack = new TimeTracker("dot diagram generation")
private[this] val dotRunTrack = new TimeTracker("dot process runnning")
private[this] val svgTrack = new TimeTracker("svg processing")
+ private[this] var brokenImages = 0
+ private[this] var fixedImages = 0
def printStats(settings: Settings) = {
if (settings.docDiagramsDebug.value) {
@@ -47,6 +49,9 @@ object DiagramStats {
dotGenTrack.printStats(settings.printMsg)
dotRunTrack.printStats(settings.printMsg)
svgTrack.printStats(settings.printMsg)
+ println(" Broken images: " + brokenImages)
+ println(" Fixed images: " + fixedImages)
+ println("")
}
}
@@ -55,4 +60,7 @@ object DiagramStats {
def addDotGenerationTime(ms: Long) = dotGenTrack.addTime(ms)
def addDotRunningTime(ms: Long) = dotRunTrack.addTime(ms)
def addSvgTime(ms: Long) = svgTrack.addTime(ms)
+
+ def addBrokenImage(): Unit = brokenImages += 1
+ def addFixedImage(): Unit = fixedImages += 1
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala
index f5a1d1b088..dc6f941c30 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala
@@ -66,10 +66,6 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
var superClasses = List[Node]()
var incomingImplicits = List[Node]()
var outgoingImplicits = List[Node]()
- var subClassesTooltip: Option[String] = None
- var superClassesTooltip: Option[String] = None
- var incomingImplicitsTooltip: Option[String] = None
- var outgoingImplicitsTooltip: Option[String] = None
isClassDiagram = false
d match {
@@ -89,23 +85,23 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
// if there are too many super / sub / implicit nodes, represent
// them by on node with a corresponding tooltip
superClasses = if (_superClasses.length > settings.docDiagramsMaxNormalClasses.value) {
- superClassesTooltip = Some(limitSize(_superClasses.map(_.tpe.name).mkString(", ")))
- List(NormalNode(textTypeEntity(_superClasses.length + MultiSuffix), None))
+ val superClassesTooltip = Some(limitSize(_superClasses.map(_.tpe.name).mkString(", ")))
+ List(NormalNode(textTypeEntity(_superClasses.length + MultiSuffix), None, superClassesTooltip))
} else _superClasses
subClasses = if (_subClasses.length > settings.docDiagramsMaxNormalClasses.value) {
- subClassesTooltip = Some(limitSize(_subClasses.map(_.tpe.name).mkString(", ")))
- List(NormalNode(textTypeEntity(_subClasses.length + MultiSuffix), None))
+ val subClassesTooltip = Some(limitSize(_subClasses.map(_.tpe.name).mkString(", ")))
+ List(NormalNode(textTypeEntity(_subClasses.length + MultiSuffix), None, subClassesTooltip))
} else _subClasses
incomingImplicits = if (_incomingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) {
- incomingImplicitsTooltip = Some(limitSize(_incomingImplicits.map(_.tpe.name).mkString(", ")))
- List(ImplicitNode(textTypeEntity(_incomingImplicits.length + MultiSuffix), None))
+ val incomingImplicitsTooltip = Some(limitSize(_incomingImplicits.map(_.tpe.name).mkString(", ")))
+ List(ImplicitNode(textTypeEntity(_incomingImplicits.length + MultiSuffix), None, incomingImplicitsTooltip))
} else _incomingImplicits
outgoingImplicits = if (_outgoingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) {
- outgoingImplicitsTooltip = Some(limitSize(_outgoingImplicits.map(_.tpe.name).mkString(", ")))
- List(ImplicitNode(textTypeEntity(_outgoingImplicits.length + MultiSuffix), None))
+ val outgoingImplicitsTooltip = Some(limitSize(_outgoingImplicits.map(_.tpe.name).mkString(", ")))
+ List(ImplicitNode(textTypeEntity(_outgoingImplicits.length + MultiSuffix), None, outgoingImplicitsTooltip))
} else _outgoingImplicits
thisNode = _thisNode
@@ -128,14 +124,14 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
// dot cluster containing thisNode
val thisCluster = "subgraph clusterThis {\n" +
"style=\"invis\"\n" +
- node2Dot(thisNode, None) +
+ node2Dot(thisNode) +
"}"
// dot cluster containing incoming implicit nodes, if any
val incomingCluster = {
if(incomingImplicits.isEmpty) ""
else "subgraph clusterIncoming {\n" +
"style=\"invis\"\n" +
- incomingImplicits.reverse.map(n => node2Dot(n, incomingImplicitsTooltip)).mkString +
+ incomingImplicits.reverse.map(n => node2Dot(n)).mkString +
(if (incomingImplicits.size > 1)
incomingImplicits.map(n => "node" + node2Index(n)).mkString(" -> ") +
" [constraint=\"false\", style=\"invis\", minlen=\"0.0\"];\n"
@@ -147,7 +143,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
if(outgoingImplicits.isEmpty) ""
else "subgraph clusterOutgoing {\n" +
"style=\"invis\"\n" +
- outgoingImplicits.reverse.map(n => node2Dot(n, outgoingImplicitsTooltip)).mkString +
+ outgoingImplicits.reverse.map(n => node2Dot(n)).mkString +
(if (outgoingImplicits.size > 1)
outgoingImplicits.map(n => "node" + node2Index(n)).mkString(" -> ") +
" [constraint=\"false\", style=\"invis\", minlen=\"0.0\"];\n"
@@ -189,9 +185,9 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
"edge [" + edgeAttributesStr + "];\n" +
implicitsDot + "\n" +
// inheritance nodes
- nodes.map(n => node2Dot(n, None)).mkString +
- subClasses.map(n => node2Dot(n, subClassesTooltip)).mkString +
- superClasses.map(n => node2Dot(n, superClassesTooltip)).mkString +
+ nodes.map(n => node2Dot(n)).mkString +
+ subClasses.map(n => node2Dot(n)).mkString +
+ superClasses.map(n => node2Dot(n)).mkString +
// inheritance edges
edges.map{ case (from, tos) => tos.map(to => {
val id = "graph" + counter + "_" + node2Index(to) + "_" + node2Index(from)
@@ -213,7 +209,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
/**
* Generates the dot string of a given node.
*/
- private def node2Dot(node: Node, tooltip: Option[String]) = {
+ private def node2Dot(node: Node) = {
// escape HTML characters in node names
def escape(name: String) = name.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
@@ -228,7 +224,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
}
// tooltip
- tooltip match {
+ node.tooltip match {
case Some(text) => attr += "tooltip" -> text
// show full name where available (instead of TraversableOps[A] show scala.collection.parallel.TraversableOps[A])
case None if node.tpl.isDefined => attr += "tooltip" -> node.tpl.get.qualifiedName
@@ -294,25 +290,23 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
*/
private def cssClass(node: Node): String =
if (node.isImplicitNode && incomingImplicitNodes.contains(node))
- "implicit-incoming"
+ "implicit-incoming" + cssBaseClass(node, "", " ")
else if (node.isImplicitNode)
- "implicit-outgoing"
- else if (node.isObjectNode)
- "object"
+ "implicit-outgoing" + cssBaseClass(node, "", " ")
else if (node.isThisNode)
- "this" + cssBaseClass(node, "")
+ "this" + cssBaseClass(node, "", " ")
else if (node.isOutsideNode)
- "outside" + cssBaseClass(node, "")
+ "outside" + cssBaseClass(node, "", " ")
else
- cssBaseClass(node, "default")
+ cssBaseClass(node, "default", "")
- private def cssBaseClass(node: Node, default: String) =
+ private def cssBaseClass(node: Node, default: String, space: String) =
if (node.isClassNode)
- " class"
+ space + "class"
else if (node.isTraitNode)
- " trait"
+ space + "trait"
else if (node.isObjectNode)
- " trait"
+ space + "object"
else
default
@@ -381,10 +375,31 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
// assign id and class attributes to edges and nodes:
// the id attribute generated by dot has the format: "{class}|{id}"
case g @ Elem(prefix, "g", attribs, scope, children @ _*) if (List("edge", "node").contains((g \ "@class").toString)) => {
- val res = new Elem(prefix, "g", attribs, scope, (children map(x => transform(x))): _*)
+ var res = new Elem(prefix, "g", attribs, scope, (children map(x => transform(x))): _*)
val dotId = (g \ "@id").toString
if (dotId.count(_ == '|') == 1) {
val Array(klass, id) = dotId.toString.split("\\|")
+ /* Sometimes dot "forgets" to add the image -- that's very annoying, but it seems pretty random, and simple
+ * tests like excute 20K times and diff the output don't trigger the bug -- so it's up to us to place the image
+ * back in the node */
+ val kind = getKind(klass)
+ if (kind != "")
+ if (((g \ "a" \ "image").isEmpty)) {
+ DiagramStats.addBrokenImage()
+ val xposition = getPosition(g, "x", -22)
+ val yposition = getPosition(g, "y", -11.3334)
+ if (xposition.isDefined && yposition.isDefined) {
+ val imageNode = <image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href={ ("./lib/" + kind + "_diagram.png") } width="16px" height="16px" preserveAspectRatio="xMinYMin meet" x={ xposition.get.toString } y={ yposition.get.toString }/>
+ val anchorNode = (g \ "a") match {
+ case Seq(Elem(prefix, "a", attribs, scope, children @ _*)) =>
+ transform(new Elem(prefix, "a", attribs, scope, (children ++ imageNode): _*))
+ case _ =>
+ g \ "a"
+ }
+ res = new Elem(prefix, "g", attribs, scope, anchorNode: _*)
+ DiagramStats.addFixedImage()
+ }
+ }
res % new UnprefixedAttribute("id", id, Null) %
new UnprefixedAttribute("class", (g \ "@class").toString + " " + klass, Null)
}
@@ -399,6 +414,20 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
case x => x
}
+ def getKind(klass: String): String =
+ if (klass.contains("class")) "class"
+ else if (klass.contains("trait")) "trait"
+ else if (klass.contains("object")) "object"
+ else ""
+
+ def getPosition(g: xml.Node, axis: String, offset: Double): Option[Double] = {
+ val node = g \ "a" \ "text" \ ("@" + axis)
+ if (node.isEmpty)
+ None
+ else
+ Some(node.toString.toDouble + offset)
+ }
+
/* graph / node / edge attributes */
private val graphAttributes: Map[String, String] = Map(
diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala
index e1bbf28dc7..2901daafd6 100644
--- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala
@@ -278,11 +278,11 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity {
def implicitsShadowing: Map[MemberEntity, ImplicitMemberShadowing]
/** Classes that can be implcitly converted to this class */
- def incomingImplicitlyConvertedClasses: List[DocTemplateEntity]
+ def incomingImplicitlyConvertedClasses: List[(DocTemplateEntity, ImplicitConversion)]
/** Classes to which this class can be implicitly converted to
NOTE: Some classes might not be included in the scaladoc run so they will be NoDocTemplateEntities */
- def outgoingImplicitlyConvertedClasses: List[(TemplateEntity, TypeEntity)]
+ def outgoingImplicitlyConvertedClasses: List[(TemplateEntity, TypeEntity, ImplicitConversion)]
/** If this template takes place in inheritance and implicit conversion relations, it will be shown in this diagram */
def inheritanceDiagram: Option[Diagram]
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
index 57625a5e15..9fa6619e9f 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
@@ -299,14 +299,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
def directSubClasses = allSubClasses.filter(_.parentTypes.map(_._1).contains(this))
/* Implcitly convertible class cache */
- private var implicitlyConvertibleClassesCache: mutable.ListBuffer[DocTemplateEntity] = null
- def registerImplicitlyConvertibleClass(sc: DocTemplateEntity): Unit = {
+ private var implicitlyConvertibleClassesCache: mutable.ListBuffer[(DocTemplateEntity, ImplicitConversionImpl)] = null
+ def registerImplicitlyConvertibleClass(dtpl: DocTemplateEntity, conv: ImplicitConversionImpl): Unit = {
if (implicitlyConvertibleClassesCache == null)
- implicitlyConvertibleClassesCache = mutable.ListBuffer[DocTemplateEntity]()
- implicitlyConvertibleClassesCache += sc
+ implicitlyConvertibleClassesCache = mutable.ListBuffer[(DocTemplateEntity, ImplicitConversionImpl)]()
+ implicitlyConvertibleClassesCache += ((dtpl, conv))
}
- def incomingImplicitlyConvertedClasses: List[DocTemplateEntity] =
+ def incomingImplicitlyConvertedClasses: List[(DocTemplateEntity, ImplicitConversionImpl)] =
if (implicitlyConvertibleClassesCache == null)
List()
else
@@ -363,18 +363,19 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
var implicitsShadowing = Map[MemberEntity, ImplicitMemberShadowing]()
- lazy val outgoingImplicitlyConvertedClasses: List[(TemplateEntity, TypeEntity)] = conversions flatMap (conv =>
- if (!implicitExcluded(conv.conversionQualifiedName))
- conv.targetTypeComponents map {
- case pair@(template, tpe) =>
- template match {
- case d: DocTemplateImpl => d.registerImplicitlyConvertibleClass(this)
- case _ => // nothing
- }
- pair
- }
- else List()
- )
+ lazy val outgoingImplicitlyConvertedClasses: List[(TemplateEntity, TypeEntity, ImplicitConversionImpl)] =
+ conversions flatMap (conv =>
+ if (!implicitExcluded(conv.conversionQualifiedName))
+ conv.targetTypeComponents map {
+ case pair@(template, tpe) =>
+ template match {
+ case d: DocTemplateImpl => d.registerImplicitlyConvertibleClass(this, conv)
+ case _ => // nothing
+ }
+ (pair._1, pair._2, conv)
+ }
+ else List()
+ )
override def isTemplate = true
lazy val definitionName = optimize(inDefinitionTemplates.head.qualifiedName + "." + name)
@@ -862,7 +863,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
// nameBuffer append stripPrefixes.foldLeft(pre.prefixString)(_ stripPrefix _)
// }
val bSym = normalizeTemplate(aSym)
- if (bSym.isNonClassType) {
+ if (bSym.isNonClassType && bSym != AnyRefClass) {
nameBuffer append bSym.decodedName
} else {
val tpl = makeTemplate(bSym)
diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
index d80999e149..8527ca4039 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala
@@ -73,6 +73,7 @@ abstract class Node {
def isOtherNode = !(isClassNode || isTraitNode || isObjectNode)
def isImplicitNode = false
def isOutsideNode = false
+ def tooltip: Option[String]
}
// different matchers, allowing you to use the pattern matcher against any node
@@ -97,20 +98,20 @@ object OtherNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEn
/** The node for the current class */
-case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node { override def isThisNode = true }
+case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isThisNode = true }
/** The usual node */
-case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node { override def isNormalNode = true }
+case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isNormalNode = true }
/** A class or trait the thisnode can be converted to by an implicit conversion
* TODO: I think it makes more sense to use the tpe links to templates instead of the TemplateEntity for implicit nodes
* since some implicit conversions convert the class to complex types that cannot be represented as a single tmeplate
*/
-case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node { override def isImplicitNode = true }
+case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isImplicitNode = true }
/** An outside node is shown in packages when a class from a different package makes it to the package diagram due to
* its relation to a class in the template (see @contentDiagram hideInheritedNodes annotation) */
-case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node { override def isOutsideNode = true }
+case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isOutsideNode = true }
// Computing and offering node depth information
diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
index 3f054b969b..1a8ad193aa 100644
--- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala
@@ -43,12 +43,16 @@ trait DiagramFactory extends DiagramDirectiveParser {
// the diagram filter
val diagramFilter = makeInheritanceDiagramFilter(tpl)
+ def implicitTooltip(from: DocTemplateEntity, to: TemplateEntity, conv: ImplicitConversion) =
+ Some(from.qualifiedName + " can be implicitly converted to " + conv.targetType + " by the implicit method "
+ + conv.conversionShortName + " in " + conv.convertorOwner.kind + " " + conv.convertorOwner.qualifiedName)
+
val result =
if (diagramFilter == NoDiagramAtAll)
None
else {
// the main node
- val thisNode = ThisNode(tpl.ownType, Some(tpl))
+ val thisNode = ThisNode(tpl.ownType, Some(tpl), Some(tpl.qualifiedName + " (this " + tpl.kind + ")"))
// superclasses
var superclasses: List[Node] =
@@ -57,7 +61,10 @@ trait DiagramFactory extends DiagramDirectiveParser {
}.reverse
// incoming implcit conversions
- lazy val incomingImplicitNodes = tpl.incomingImplicitlyConvertedClasses.map(tpl => ImplicitNode(tpl.ownType, Some(tpl)))
+ lazy val incomingImplicitNodes = tpl.incomingImplicitlyConvertedClasses.map {
+ case (incomingTpl, conv) =>
+ ImplicitNode(incomingTpl.ownType, Some(incomingTpl), implicitTooltip(from=incomingTpl, to=tpl, conv=conv))
+ }
// subclasses
var subclasses: List[Node] =
@@ -67,7 +74,10 @@ trait DiagramFactory extends DiagramDirectiveParser {
}.sortBy(_.tpl.get.name)(implicitly[Ordering[String]].reverse)
// outgoing implicit coversions
- lazy val outgoingImplicitNodes = tpl.outgoingImplicitlyConvertedClasses.map(pair => ImplicitNode(pair._2, Some(pair._1)))
+ lazy val outgoingImplicitNodes = tpl.outgoingImplicitlyConvertedClasses.map {
+ case (outgoingTpl, outgoingType, conv) =>
+ ImplicitNode(outgoingType, Some(outgoingTpl), implicitTooltip(from=tpl, to=tpl, conv=conv))
+ }
// TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams.
// Currently, it's possible to leave nodes and edges out, but there's no way to create new nodes and edges
diff --git a/test/scaladoc/resources/doc-root/Any.scala b/test/scaladoc/resources/doc-root/Any.scala
new file mode 100644
index 0000000000..031b7d9d8c
--- /dev/null
+++ b/test/scaladoc/resources/doc-root/Any.scala
@@ -0,0 +1,114 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+/** Class `Any` is the root of the Scala class hierarchy. Every class in a Scala
+ * execution environment inherits directly or indirectly from this class.
+ */
+abstract class Any {
+ /** Compares the receiver object (`this`) with the argument object (`that`) for equivalence.
+ *
+ * Any implementation of this method should be an [[http://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]]:
+ *
+ * - It is reflexive: for any instance `x` of type `Any`, `x.equals(x)` should return `true`.
+ * - It is symmetric: for any instances `x` and `y` of type `Any`, `x.equals(y)` should return `true` if and
+ * only if `y.equals(x)` returns `true`.
+ * - It is transitive: for any instances `x`, `y`, and `z` of type `AnyRef` if `x.equals(y)` returns `true` and
+ * `y.equals(z)` returns `true`, then `x.equals(z)` should return `true`.
+ *
+ * If you override this method, you should verify that your implementation remains an equivalence relation.
+ * Additionally, when overriding this method it is usually necessary to override `hashCode` to ensure that
+ * objects which are "equal" (`o1.equals(o2)` returns `true`) hash to the same [[scala.Int]].
+ * (`o1.hashCode.equals(o2.hashCode)`).
+ *
+ * @param that the object to compare against this object for equality.
+ * @return `true` if the receiver object is equivalent to the argument; `false` otherwise.
+ */
+ def equals(that: Any): Boolean
+
+ /** Calculate a hash code value for the object.
+ *
+ * The default hashing algorithm is platform dependent.
+ *
+ * Note that it is allowed for two objects to have identical hash codes (`o1.hashCode.equals(o2.hashCode)`) yet
+ * not be equal (`o1.equals(o2)` returns `false`). A degenerate implementation could always return `0`.
+ * However, it is required that if two objects are equal (`o1.equals(o2)` returns `true`) that they have
+ * identical hash codes (`o1.hashCode.equals(o2.hashCode)`). Therefore, when overriding this method, be sure
+ * to verify that the behavior is consistent with the `equals` method.
+ *
+ * @return the hash code value for this object.
+ */
+ def hashCode(): Int
+
+ /** Returns a string representation of the object.
+ *
+ * The default representation is platform dependent.
+ *
+ * @return a string representation of the object.
+ */
+ def toString(): String
+
+ /** Returns the runtime class representation of the object.
+ *
+ * @return a class object corresponding to the runtime type of the receiver.
+ */
+ def getClass(): Class[_]
+
+ /** Test two objects for equality.
+ * The expression `x == that` is equivalent to `if (x eq null) that eq null else x.equals(that)`.
+ *
+ * @param that the object to compare against this object for equality.
+ * @return `true` if the receiver object is equivalent to the argument; `false` otherwise.
+ */
+ final def ==(that: Any): Boolean = this equals that
+
+ /** Test two objects for inequality.
+ *
+ * @param that the object to compare against this object for equality.
+ * @return `true` if !(this == that), false otherwise.
+ */
+ final def != (that: Any): Boolean = !(this == that)
+
+ /** Equivalent to `x.hashCode` except for boxed numeric types and `null`.
+ * For numerics, it returns a hash value which is consistent
+ * with value equality: if two value type instances compare
+ * as true, then ## will produce the same hash value for each
+ * of them.
+ * For `null` returns a hashcode where `null.hashCode` throws a
+ * `NullPointerException`.
+ *
+ * @return a hash value consistent with ==
+ */
+ final def ##(): Int = sys.error("##")
+
+ /** Test whether the dynamic type of the receiver object is `T0`.
+ *
+ * Note that the result of the test is modulo Scala's erasure semantics.
+ * Therefore the expression `1.isInstanceOf[String]` will return `false`, while the
+ * expression `List(1).isInstanceOf[List[String]]` will return `true`.
+ * In the latter example, because the type argument is erased as part of compilation it is
+ * not possible to check whether the contents of the list are of the specified type.
+ *
+ * @return `true` if the receiver object is an instance of erasure of type `T0`; `false` otherwise.
+ */
+ def isInstanceOf[T0]: Boolean = sys.error("isInstanceOf")
+
+ /** Cast the receiver object to be of type `T0`.
+ *
+ * Note that the success of a cast at runtime is modulo Scala's erasure semantics.
+ * Therefore the expression `1.asInstanceOf[String]` will throw a `ClassCastException` at
+ * runtime, while the expression `List(1).asInstanceOf[List[String]]` will not.
+ * In the latter example, because the type argument is erased as part of compilation it is
+ * not possible to check whether the contents of the list are of the requested type.
+ *
+ * @throws ClassCastException if the receiver object is not an instance of the erasure of type `T0`.
+ * @return the receiver object.
+ */
+ def asInstanceOf[T0]: T0 = sys.error("asInstanceOf")
+}
diff --git a/test/scaladoc/resources/doc-root/AnyRef.scala b/test/scaladoc/resources/doc-root/AnyRef.scala
new file mode 100644
index 0000000000..1eefb0c806
--- /dev/null
+++ b/test/scaladoc/resources/doc-root/AnyRef.scala
@@ -0,0 +1,131 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+/** Class `AnyRef` is the root class of all ''reference types''.
+ * All types except the value types descend from this class.
+ */
+trait AnyRef extends Any {
+
+ /** The equality method for reference types. Default implementation delegates to `eq`.
+ *
+ * See also `equals` in [[scala.Any]].
+ *
+ * @param that the object to compare against this object for equality.
+ * @return `true` if the receiver object is equivalent to the argument; `false` otherwise.
+ */
+ def equals(that: Any): Boolean = this eq that
+
+ /** The hashCode method for reference types. See hashCode in [[scala.Any]].
+ *
+ * @return the hash code value for this object.
+ */
+ def hashCode: Int = sys.error("hashCode")
+
+ /** Creates a String representation of this object. The default
+ * representation is platform dependent. On the java platform it
+ * is the concatenation of the class name, "@", and the object's
+ * hashcode in hexadecimal.
+ *
+ * @return a String representation of the object.
+ */
+ def toString: String = sys.error("toString")
+
+ /** Executes the code in `body` with an exclusive lock on `this`.
+ *
+ * @param body the code to execute
+ * @return the result of `body`
+ */
+ def synchronized[T](body: => T): T
+
+ /** Tests whether the argument (`arg0`) is a reference to the receiver object (`this`).
+ *
+ * The `eq` method implements an [[http://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]] on
+ * non-null instances of `AnyRef`, and has three additional properties:
+ *
+ * - It is consistent: for any non-null instances `x` and `y` of type `AnyRef`, multiple invocations of
+ * `x.eq(y)` consistently returns `true` or consistently returns `false`.
+ * - For any non-null instance `x` of type `AnyRef`, `x.eq(null)` and `null.eq(x)` returns `false`.
+ * - `null.eq(null)` returns `true`.
+ *
+ * When overriding the `equals` or `hashCode` methods, it is important to ensure that their behavior is
+ * consistent with reference equality. Therefore, if two objects are references to each other (`o1 eq o2`), they
+ * should be equal to each other (`o1 == o2`) and they should hash to the same value (`o1.hashCode == o2.hashCode`).
+ *
+ * @param that the object to compare against this object for reference equality.
+ * @return `true` if the argument is a reference to the receiver object; `false` otherwise.
+ */
+ final def eq(that: AnyRef): Boolean = sys.error("eq")
+
+ /** Equivalent to `!(this eq that)`.
+ *
+ * @param that the object to compare against this object for reference equality.
+ * @return `true` if the argument is not a reference to the receiver object; `false` otherwise.
+ */
+ final def ne(that: AnyRef): Boolean = !(this eq that)
+
+ /** The expression `x == that` is equivalent to `if (x eq null) that eq null else x.equals(that)`.
+ *
+ * @param arg0 the object to compare against this object for equality.
+ * @return `true` if the receiver object is equivalent to the argument; `false` otherwise.
+ */
+ final def ==(that: AnyRef): Boolean =
+ if (this eq null) that eq null
+ else this equals that
+
+ /** Create a copy of the receiver object.
+ *
+ * The default implementation of the `clone` method is platform dependent.
+ *
+ * @note not specified by SLS as a member of AnyRef
+ * @return a copy of the receiver object.
+ */
+ protected def clone(): AnyRef
+
+ /** Called by the garbage collector on the receiver object when there
+ * are no more references to the object.
+ *
+ * The details of when and if the `finalize` method is invoked, as
+ * well as the interaction between `finalize` and non-local returns
+ * and exceptions, are all platform dependent.
+ *
+ * @note not specified by SLS as a member of AnyRef
+ */
+ protected def finalize(): Unit
+
+ /** A representation that corresponds to the dynamic class of the receiver object.
+ *
+ * The nature of the representation is platform dependent.
+ *
+ * @note not specified by SLS as a member of AnyRef
+ * @return a representation that corresponds to the dynamic class of the receiver object.
+ */
+ def getClass(): Class[_]
+
+ /** Wakes up a single thread that is waiting on the receiver object's monitor.
+ *
+ * @note not specified by SLS as a member of AnyRef
+ */
+ def notify(): Unit
+
+ /** Wakes up all threads that are waiting on the receiver object's monitor.
+ *
+ * @note not specified by SLS as a member of AnyRef
+ */
+ def notifyAll(): Unit
+
+ /** Causes the current Thread to wait until another Thread invokes
+ * the notify() or notifyAll() methods.
+ *
+ * @note not specified by SLS as a member of AnyRef
+ */
+ def wait (): Unit
+ def wait (timeout: Long, nanos: Int): Unit
+ def wait (timeout: Long): Unit
+}
diff --git a/test/scaladoc/resources/doc-root/Nothing.scala b/test/scaladoc/resources/doc-root/Nothing.scala
new file mode 100644
index 0000000000..eed6066039
--- /dev/null
+++ b/test/scaladoc/resources/doc-root/Nothing.scala
@@ -0,0 +1,23 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+/** `Nothing` is - together with [[scala.Null]] - at the bottom of Scala's type hierarchy.
+ *
+ * `Nothing` is a subtype of every other type (including [[scala.Null]]); there exist
+ * ''no instances'' of this type. Although type `Nothing` is uninhabited, it is
+ * nevertheless useful in several ways. For instance, the Scala library defines a value
+ * [[scala.collection.immutable.Nil]] of type `List[Nothing]`. Because lists are covariant in Scala,
+ * this makes [[scala.collection.immutable.Nil]] an instance of `List[T]`, for any element of type `T`.
+ *
+ * Another usage for Nothing is the return type for methods which never return normally.
+ * One example is method error in [[scala.sys]], which always throws an exception.
+ */
+sealed trait Nothing
+
diff --git a/test/scaladoc/resources/doc-root/Null.scala b/test/scaladoc/resources/doc-root/Null.scala
new file mode 100644
index 0000000000..7455e78ae7
--- /dev/null
+++ b/test/scaladoc/resources/doc-root/Null.scala
@@ -0,0 +1,17 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2010, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+/** `Null` is - together with [[scala.Nothing]] - at the bottom of the Scala type hierarchy.
+ *
+ * `Null` is a subtype of all reference types; its only instance is the `null` reference.
+ * Since `Null` is not a subtype of value types, `null` is not a member of any such type. For instance,
+ * it is not possible to assign `null` to a variable of type [[scala.Int]].
+ */
+sealed trait Null
diff --git a/test/scaladoc/run/SI-5780.check b/test/scaladoc/run/SI-5780.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/SI-5780.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/SI-5780.scala b/test/scaladoc/run/SI-5780.scala
new file mode 100644
index 0000000000..809567faec
--- /dev/null
+++ b/test/scaladoc/run/SI-5780.scala
@@ -0,0 +1,25 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.nsc.doc.model.diagram._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ package scala.test.scaladoc.SI5780
+
+ object `package` { def foo: AnyRef = "hello"; class T /* so the package is not dropped */ }
+ """
+
+ // diagrams must be started. In case there's an error with dot, it should not report anything
+ def scaladocSettings = "-doc-root-content " + resourcePath + "/doc-root"
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ val foo = rootPackage._package("scala")._package("test")._package("scaladoc")._package("SI5780")._method("foo")
+ // check that AnyRef is properly linked to its template:
+ assert(foo.resultType.name == "AnyRef", foo.resultType.name + " == AnyRef")
+ assert(foo.resultType.refEntity.size == 1, foo.resultType.refEntity + ".size == 1")
+ }
+} \ No newline at end of file