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.Flags import dotc.core.Contexts.Context import dotc.core.Symbols.Symbol import dotty.tools.dotc.core.SymDenotations._ import dotty.tools.dotc.core.Names.TypeName import dotc.core.{ Flags => DottyFlags } import dotc.ast.Trees._ object factories { import dotty.tools.dotc.ast.tpd._ import dotty.tools.dottydoc.model.internal.ParamListImpl import DottyFlags._ 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(_ != "") .filter(_ != "interface") def path(sym: Symbol)(implicit ctx: Context): List[String] = sym match { case sym if sym.name.decode.toString == "" => Nil case sym => path(sym.owner) :+ sym.name.show } 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 tl: TypeLambda => //FIXME: should be handled correctly // example, in `Option`: // // {{{ // def companion: GenericCompanion[collection.Iterable] // }}} // // Becomes: def companion: [+X0] -> collection.Iterable[X0] typeRef(tl.show + " (not handled)") case AppliedType(tycon, args) => val cls = tycon.typeSymbol if (tycon.isRepeatedParam) expandTpe(args.head) else 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 = tycon.show val name = query.split("\\.").last typeRef(name, query, params = args.map(expandTpe(_, Nil))) } case ref @ RefinedType(parent, rn, info) => expandTpe(parent) //FIXME: will be a refined HK, aka class Foo[X] { def bar: List[X] } or similar case ref @ HKApply(tycon, args) => expandTpe(tycon, args.map(expandTpe(_, params))) case TypeRef(_, n) => val name = n.decode.toString.split("\\$").last typeRef(name, params = params) case ta: TypeAlias => expandTpe(ta.alias.widenDealias) case OrType(left, right) => OrTypeReference(expandTpe(left), expandTpe(right)) case AndType(left, right) => AndTypeReference(expandTpe(left), expandTpe(right)) case tb @ TypeBounds(lo, hi) => BoundsReference(expandTpe(lo), expandTpe(hi)) case AnnotatedType(tpe, _) => expandTpe(tpe) case ExprType(tpe) => expandTpe(tpe) case c: ConstantType => ConstantReference(c.show) case tt: ThisType => expandTpe(tt.underlying) case ci: ClassInfo => val query = path(ci.typeSymbol).mkString(".") typeRef(ci.cls.name.show, query = query) case mt: MethodType => expandTpe(mt.resultType) case pt: PolyType => expandTpe(pt.resultType) case pp: PolyParam => val paramName = pp.paramName.show val name = if (paramName.contains('$')) paramName.split("\\$\\$").last else paramName typeRef(name) } 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 Flags.TypeParam) .map { tp => val prefix = if (tp.flags is Flags.Covariant) "+" else if (tp.flags is Flags.Contravariant) "-" else "" prefix + tp.name.show.split("\\$").last } .toList case _ => Nil } 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 annot: AnnotatedType => paramLists(annot.tpe) case (_: PolyParam | _: RefinedType | _: TypeRef | _: ThisType | _: ExprType | _: OrType | _: AndType | _: HKApply) => 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 } }