Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

324 lines (295 sloc) 13.7 kb
/* NSC -- new Scala compiler -- Copyright 2007-2012 LAMP/EPFL */
package scala.tools.nsc
package doc
package model
import comment._
import diagram._
import scala.collection._
import scala.util.matching.Regex
import symtab.Flags
import io._
import model.{ RootPackage => RootPackageEntity }
/** This trait extracts all required information for documentation from compilation units */
trait ModelFactoryTypeSupport {
thisFactory: ModelFactory
with ModelFactoryImplicitSupport
with ModelFactoryTypeSupport
with DiagramFactory
with CommentFactory
with TreeFactory =>
import global._
import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass }
import rootMirror.{ RootPackage, RootClass, EmptyPackage }
protected val typeCache = new mutable.LinkedHashMap[Type, TypeEntity]
/** */
def makeType(aType: Type, inTpl: TemplateImpl): TypeEntity = {
def templatePackage = closestPackage(inTpl.sym)
def createTypeEntity = new TypeEntity {
private var nameBuffer = new StringBuilder
private var refBuffer = new immutable.TreeMap[Int, (LinkTo, Int)]
private def appendTypes0(types: List[Type], sep: String): Unit = types match {
case Nil =>
case tp :: Nil =>
appendType0(tp)
case tp :: tps =>
appendType0(tp)
nameBuffer append sep
appendTypes0(tps, sep)
}
private def appendType0(tpe: Type): Unit = tpe match {
/* Type refs */
case tp: TypeRef if definitions.isFunctionType(tp) =>
val args = tp.normalize.typeArgs
nameBuffer append '('
appendTypes0(args.init, ", ")
nameBuffer append ") ⇒ "
appendType0(args.last)
case tp: TypeRef if definitions.isScalaRepeatedParamType(tp) =>
appendType0(tp.args.head)
nameBuffer append '*'
case tp: TypeRef if definitions.isByNameParamType(tp) =>
nameBuffer append ""
appendType0(tp.args.head)
case tp: TypeRef if definitions.isTupleType(tp) =>
val args = tp.normalize.typeArgs
nameBuffer append '('
appendTypes0(args, ", ")
nameBuffer append ')'
case TypeRef(pre, aSym, targs) =>
val preSym = pre.widen.typeSymbol
// SI-3314/SI-4888: Classes, Traits and Types can be inherited from a template to another:
// class Enum { abstract class Value }
// class Day extends Enum { object Mon extends Value /*...*/ }
// ===> in such cases we have two options:
// (0) if there's no inheritance taking place (Enum#Value) we can link to the template directly
// (1) if we generate the doc template for Day, we can link to the correct member
// (2) If the symbol comes from an external library for which we know the documentation URL, point to it.
// (3) if we don't generate the doc template, we should at least indicate the correct prefix in the tooltip
val bSym = normalizeTemplate(aSym)
val owner =
if ((preSym != NoSymbol) && /* it needs a prefix */
(preSym != bSym.owner) && /* prefix is different from owner */
(aSym == bSym)) /* normalization doesn't play tricks on us */
preSym
else
bSym.owner
val link =
findTemplateMaybe(bSym) match {
case Some(bTpl) if owner == bSym.owner =>
// (0) the owner's class is linked AND has a template - lovely
LinkToTpl(bTpl)
case _ =>
val oTpl = findTemplateMaybe(owner)
(oTpl, oTpl flatMap (findMember(bSym, _))) match {
case (Some(oTpl), Some(bMbr)) =>
// (1) the owner's class
LinkToMember(bMbr, oTpl)
case _ =>
val name = makeQualifiedName(bSym)
if (!bSym.owner.isPackage)
Tooltip(name)
else
findExternalLink(bSym, name).getOrElse (
// (3) if we couldn't find neither the owner nor external URL to link to, show a tooltip with the qualified name
Tooltip(name)
)
}
}
// SI-4360 Showing prefixes when necessary
// We check whether there's any directly accessible type with the same name in the current template OR if the
// type is inherited from one template to another. There may be multiple symbols with the same name in scope,
// but we won't show the prefix if our symbol is among them, only if *it's not* -- that's equal to showing
// the prefix only for ambiguous references, not for overloaded ones.
def needsPrefix: Boolean = {
if ((owner != bSym.owner || preSym.isRefinementClass) && (normalizeTemplate(owner) != inTpl.sym))
return true
// don't get tricked into prefixng method type params and existentials:
// I tried several tricks BUT adding the method for which I'm creating the type => that simply won't scale,
// as ValueParams are independent of their parent member, and I really don't want to add this information to
// all terms, as we're already over the allowed memory footprint
if (aSym.isTypeParameterOrSkolem || aSym.isExistentiallyBound /* existential or existential skolem */)
return false
for (tpl <- inTpl.sym.ownerChain) {
tpl.info.member(bSym.name) match {
case NoSymbol =>
// No syms with that name, look further inside the owner chain
case sym =>
// Symbol found -- either the correct symbol, another one OR an overloaded alternative
if (sym == bSym)
return false
else sym.info match {
case OverloadedType(owner, alternatives) =>
return alternatives.contains(bSym)
case _ =>
return true
}
}
}
// if it's not found in the owner chain, we can safely leave out the prefix
false
}
val prefix =
if (!settings.docNoPrefixes.value && needsPrefix && (bSym != AnyRefClass /* which we normalize */)) {
if (!owner.isRefinementClass) {
val qName = makeQualifiedName(owner, Some(inTpl.sym))
if (qName != "") qName + "." else ""
}
else {
nameBuffer append "("
appendType0(pre)
nameBuffer append ")#"
"" // we already appended the prefix
}
} else ""
//DEBUGGING:
//if (makeQualifiedName(bSym) == "pack1.A") println("needsPrefix(" + bSym + ", " + owner + ", " + inTpl.qualifiedName + ") => " + needsPrefix + " and prefix=" + prefix)
val name = prefix + bSym.nameString
val pos0 = nameBuffer.length
refBuffer += pos0 -> ((link, name.length))
nameBuffer append name
if (!targs.isEmpty) {
nameBuffer append '['
appendTypes0(targs, ", ")
nameBuffer append ']'
}
/* Refined types */
case RefinedType(parents, defs) =>
val ignoreParents = Set[Symbol](AnyClass, ObjectClass)
val filtParents = parents filterNot (x => ignoreParents(x.typeSymbol)) match {
case Nil => parents
case ps => ps
}
appendTypes0(filtParents, " with ")
// XXX Still todo: properly printing refinements.
// Since I didn't know how to go about displaying a multi-line type, I went with
// printing single method refinements (which should be the most common) and printing
// the number of members if there are more.
defs.toList match {
case Nil => ()
case x :: Nil => nameBuffer append (" { " + x.defString + " }")
case xs => nameBuffer append (" { ... /* %d definitions in type refinement */ }" format xs.size)
}
/* Eval-by-name types */
case NullaryMethodType(result) =>
nameBuffer append '⇒'
appendType0(result)
/* Polymorphic types */
case PolyType(tparams, result) => assert(tparams.nonEmpty)
def typeParamsToString(tps: List[Symbol]): String = if (tps.isEmpty) "" else
tps.map{tparam =>
tparam.varianceString + tparam.name + typeParamsToString(tparam.typeParams)
}.mkString("[", ", ", "]")
nameBuffer append typeParamsToString(tparams)
appendType0(result)
case et@ExistentialType(quantified, underlying) =>
def appendInfoStringReduced(sym: Symbol, tp: Type): Unit = {
if (sym.isType && !sym.isAliasType && !sym.isClass) {
tp match {
case PolyType(tparams, _) =>
nameBuffer append "["
appendTypes0(tparams.map(_.tpe), ", ")
nameBuffer append "]"
case _ =>
}
tp.resultType match {
case rt @ TypeBounds(_, _) =>
appendType0(rt)
case rt =>
nameBuffer append " <: "
appendType0(rt)
}
} else {
// fallback to the Symbol infoString
nameBuffer append sym.infoString(tp)
}
}
def appendClauses = {
nameBuffer append " forSome {"
var first = true
val qset = quantified.toSet
for (sym <- quantified) {
if (!first) { nameBuffer append ", " } else first = false
if (sym.isSingletonExistential) {
nameBuffer append "val "
nameBuffer append tpnme.dropSingletonName(sym.name)
nameBuffer append ": "
appendType0(dropSingletonType(sym.info.bounds.hi))
} else {
if (sym.flagString != "") nameBuffer append (sym.flagString + " ")
if (sym.keyString != "") nameBuffer append (sym.keyString + " ")
nameBuffer append sym.varianceString
nameBuffer append sym.nameString
appendInfoStringReduced(sym, sym.info)
}
}
nameBuffer append "}"
}
underlying match {
case TypeRef(pre, sym, args) if et.isRepresentableWithWildcards =>
appendType0(typeRef(pre, sym, Nil))
nameBuffer append "["
var first = true
val qset = quantified.toSet
for (arg <- args) {
if (!first) { nameBuffer append ", " } else first = false
arg match {
case TypeRef(_, sym, _) if (qset contains sym) =>
nameBuffer append "_"
appendInfoStringReduced(sym, sym.info)
case arg =>
appendType0(arg)
}
}
nameBuffer append "]"
case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) =>
nameBuffer append "("
appendType0(underlying)
nameBuffer append ")"
appendClauses
case _ =>
appendType0(underlying)
appendClauses
}
case tb@TypeBounds(lo, hi) =>
if (tb.lo != TypeBounds.empty.lo) {
nameBuffer append " >: "
appendType0(lo)
}
if (tb.hi != TypeBounds.empty.hi) {
nameBuffer append " <: "
appendType0(hi)
}
// case tpen: ThisType | SingleType | SuperType =>
// if (tpen.isInstanceOf[ThisType] && tpen.asInstanceOf[ThisType].sym.isEffectiveRoot) {
// appendType0 typeRef(NoPrefix, sym, Nil)
// } else {
// val underlying =
// val pre = underlying.typeSymbol.skipPackageObject
// if (pre.isOmittablePrefix) pre.fullName + ".type"
// else prefixString + "type"
case tpen@ThisType(sym) =>
appendType0(typeRef(NoPrefix, sym, Nil))
nameBuffer append ".this"
if (!tpen.underlying.typeSymbol.skipPackageObject.isOmittablePrefix) nameBuffer append ".type"
case tpen@SuperType(thistpe, supertpe) =>
nameBuffer append "super["
appendType0(supertpe)
nameBuffer append "]"
case tpen@SingleType(pre, sym) =>
appendType0(typeRef(pre, sym, Nil))
if (!tpen.underlying.typeSymbol.skipPackageObject.isOmittablePrefix) nameBuffer append ".type"
case tpen =>
nameBuffer append tpen.toString
}
appendType0(aType)
val refEntity = refBuffer
val name = optimize(nameBuffer.toString)
nameBuffer = null
}
// SI-4360: Entity caching depends on both the type AND the template it's in, as the prefixes might change for the
// same type based on the template the type is shown in.
if (settings.docNoPrefixes.value)
typeCache.getOrElseUpdate(aType, createTypeEntity)
else createTypeEntity
}
}
Jump to Line
Something went wrong with that request. Please try again.