package dotty.tools.dottydoc
package model
import comment._
import references._
import dotty.tools.dotc
import dotc.core.Types._
import dotc.core.TypeApplications._
import dotc.core.Contexts.Context
import dotc.core.Symbols.{ Symbol, ClassSymbol }
import dotty.tools.dotc.core.SymDenotations._
import dotty.tools.dotc.config.Printers.dottydoc
import dotty.tools.dotc.core.Names.TypeName
import dotc.ast.Trees._
object factories {
import dotty.tools.dotc.ast.tpd._
import dotty.tools.dottydoc.model.internal.ParamListImpl
import dotc.core.Flags._
type TypeTree = dotty.tools.dotc.ast.Trees.Tree[Type]
def flags(t: Tree)(implicit ctx: Context): List[String] =
(t.symbol.flags & SourceModifierFlags)
.flagStrings.toList
.filter(_ != "<trait>")
.filter(_ != "interface")
def path(sym: Symbol)(implicit ctx: Context): List[String] = sym match {
case sym if sym.name.decode.toString == "<root>" => Nil
case sym => path(sym.owner) :+ sym.name.show
}
def annotations(sym: Symbol)(implicit ctx: Context): List[String] =
sym.annotations.map(_.symbol.showFullName)
private val product = """Product[1-9][0-9]*""".r
def returnType(t: Type)(implicit ctx: Context): Reference = {
val defn = ctx.definitions
def typeRef(name: String, query: String = "", params: List[Reference] = Nil) = {
val realQuery = if (query != "") query else name
TypeReference(name, UnsetLink(name, realQuery), params)
}
def expandTpe(t: Type, params: List[Reference] = Nil): Reference = t match {
case AppliedType(tycon, args) => {
val cls = tycon.typeSymbol
if (defn.isFunctionClass(cls))
FunctionReference(args.init.map(expandTpe(_, Nil)), expandTpe(args.last))
else if (defn.isTupleClass(cls))
TupleReference(args.map(expandTpe(_, Nil)))
else {
val query = cls.showFullName
val name = cls.name.show
typeRef(name, query, params = args.map(expandTpe(_, Nil)))
}
}
case t: TypeRef => {
val cls = t.typeSymbol
typeRef(cls.name.show.split("\\$\\$").last, query = cls.showFullName, params = params)
}
case TypeBounds(lo, hi) =>
BoundsReference(expandTpe(lo), expandTpe(hi))
case t: PolyParam =>
typeRef(t.paramName.show, params = params)
case ExprType(tpe) =>
expandTpe(tpe)
case t: ThisType =>
expandTpe(t.underlying)
case AnnotatedType(t, _) =>
expandTpe(t)
case t: MethodType =>
expandTpe(t.finalResultType)
case t: TermRef => {
/** A `TermRef` appears in the return type in e.g:
* ```
* def id[T](t: T): t.type = t
* ```
*/
val name = t.show
if (!name.endsWith(".type"))
ctx.warning(s"unhandled return type found: $t")
typeRef(name, query = t.typeSymbol.showFullName, params = params)
}
case ci: ClassInfo =>
typeRef(ci.cls.name.show, query = ci.typeSymbol.showFullName)
case tl: PolyType => {
// FIXME: should be handled correctly
// example, in `Option`:
//
// ```scala
// def companion: GenericCompanion[collection.Iterable]
// ```
//
// Becomes: def companion: [+X0] -> collection.Iterable[X0]
typeRef(tl.show + " (not handled)")
}
case OrType(left, right) =>
OrTypeReference(expandTpe(left), expandTpe(right))
case AndType(left, right) =>
AndTypeReference(expandTpe(left), expandTpe(right))
case c: ConstantType =>
ConstantReference(c.show)
case ref @ RefinedType(parent, rn, info) =>
expandTpe(parent) //FIXME: will be a refined HK, aka class Foo[X] { def bar: List[X] } or similar
}
expandTpe(t)
}
def typeParams(sym: Symbol)(implicit ctx: Context): List[String] =
sym.info match {
case pt: PolyType => // TODO: not sure if this case is needed anymore
pt.paramNames.map(_.show.split("\\$").last)
case ClassInfo(_, _, _, decls, _) =>
decls.iterator
.filter(_.flags is TypeParam)
.map { tp =>
val prefix =
if (tp.flags is Covariant) "+"
else if (tp.flags is Contravariant) "-"
else ""
prefix + tp.name.show.split("\\$").last
}
.toList
case _ =>
Nil
}
def constructors(sym: Symbol)(implicit ctx: Context): List[List[ParamList]] = sym match {
case sym: ClassSymbol =>
paramLists(sym.primaryConstructor.info) :: Nil
case _ => Nil
}
def traitParameters(sym: Symbol)(implicit ctx: Context): List[ParamList] =
constructors(sym).head
def paramLists(tpe: Type)(implicit ctx: Context): List[ParamList] = tpe match {
case pt: PolyType =>
paramLists(pt.resultType)
case mt: MethodType =>
ParamListImpl(mt.paramNames.zip(mt.paramTypes).map { case (name, tpe) =>
NamedReference(
name.decode.toString,
returnType(tpe),
isByName = tpe.isInstanceOf[ExprType],
isRepeated = tpe.isRepeatedParam
)
}, mt.isImplicit) :: paramLists(mt.resultType)
case mp: MethodParam =>
paramLists(mp.underlying)
case annot: AnnotatedType =>
paramLists(annot.tpe)
case (_: PolyParam | _: RefinedType | _: TypeRef | _: ThisType |
_: ExprType | _: OrType | _: AndType | _: HKApply | _: TermRef) =>
Nil // return types should not be in the paramlist
}
def superTypes(t: Tree)(implicit ctx: Context): List[MaterializableLink] = t.symbol.denot match {
case cd: ClassDenotation =>
def isJavaLangObject(prefix: Type): Boolean =
prefix match {
case TypeRef(ThisType(TypeRef(NoPrefix, outerName)), innerName) =>
outerName.toString == "lang" && innerName.toString == "Object"
case _ => false
}
def isProductWithArity(prefix: Type): Boolean = prefix match {
case TypeRef(TermRef(TermRef(NoPrefix, root), scala), prod) =>
root.toString == "_root_" &&
scala.toString == "scala" &&
product.findFirstIn(prod.toString).isDefined
case _ => false
}
cd.classParents.collect {
case t: TypeRef if !isJavaLangObject(t) && !isProductWithArity(t) =>
UnsetLink(t.name.toString, path(t.symbol).mkString("."))
}
case _ => Nil
}
}