All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
dotty.tools.scaladoc.tasty.TastyParser.scala Maven / Gradle / Ivy
package dotty.tools.scaladoc
package tasty
import java.util.regex.Pattern
import scala.util.{Try, Success, Failure}
import scala.tasty.inspector.{TastyInspector, Inspector, Tasty}
import scala.quoted._
import dotty.tools.dotc
import dotty.tools.scaladoc.tasty.comments.MemberLookup
import dotty.tools.scaladoc.tasty.comments.QueryParser
import dotty.tools.scaladoc.tasty.comments.Comment
import java.nio.file.Paths
import java.nio.file.Files
import SymOps._
import ScaladocSupport._
/** Responsible for collectively inspecting all the Tasty files we're interested in.
*
* Delegates most of the work to [[TastyParser]] [[dotty.tools.scaladoc.tasty.TastyParser]].
*/
case class ScaladocTastyInspector()(using ctx: DocContext) extends Inspector:
private val topLevels = Seq.newBuilder[(String, Member)]
private var rootDoc: Option[Comment] = None
def inspect(using Quotes)(tastys: List[scala.tasty.inspector.Tasty[quotes.type]]): Unit =
val symbolsToSkip: Set[reflect.Symbol] =
ctx.args.identifiersToSkip.flatMap { ref =>
val qrSymbol = reflect.Symbol
Try(qrSymbol.requiredPackage(ref)).orElse(Try(qrSymbol.requiredClass(ref))) match {
case Success(sym) => Some(sym)
case Failure(err) =>
report.warning(
s"Failed to resolve identifier to skip - $ref - because: ${throwableToString(err)}",
dotc.util.NoSourcePosition,
)
None
}
}.toSet
val patternsToSkip: List[Pattern] =
ctx.args.regexesToSkip.flatMap { regexString =>
Try(Pattern.compile(regexString)) match
case Success(pat) => Some(pat)
case Failure(err) =>
report.warning(
s"Failed to compile regex to skip - $regexString - because: ${throwableToString(err)}",
dotc.util.NoSourcePosition,
)
None
}
def isSkipped(sym: reflect.Symbol): Boolean =
def isSkippedById(sym: reflect.Symbol): Boolean =
if !sym.exists then false else
symbolsToSkip.contains(sym) || isSkipped(sym.owner)
def isSkippedByRx(sym: reflect.Symbol): Boolean =
val symStr = sym.fullName
patternsToSkip.exists(p => p.matcher(symStr).matches())
isSkippedById(sym) || isSkippedByRx(sym)
val parser = new TastyParser(quotes, this)(isSkipped)
def driFor(link: String): Option[DRI] =
val symOps = new SymOpsWithLinkCache
import symOps._
Try(QueryParser(link).readQuery()).toOption.flatMap(query =>
MemberLookup.lookupOpt(query, None).map {
case (sym, _, inheritingParent) => inheritingParent match
case Some(parent) => sym.driInContextOfInheritingParent(parent)
case None => sym.dri
}
)
ctx.staticSiteContext.foreach(_.memberLinkResolver = driFor)
var alreadyProcessed = false
def processRootDocIfNeeded(tree: parser.qctx.reflect.Tree) =
def readFile(pathStr: String)(using CompilerContext): Option[String] =
try
val path = Paths.get(pathStr)
if Files.exists(path) then Some(util.IO.read(path))
else
report.inform("Rootdoc at $pathStr does not exisits")
None
catch
case e: RuntimeException =>
report.warning(s"Unable to read root package doc from $pathStr: ${throwableToString(e)}")
None
if !alreadyProcessed then
alreadyProcessed = true
ctx.args.rootDocPath.flatMap(readFile).map { content =>
import parser.qctx.reflect._
def root(s: Symbol): Symbol = if s.owner.isNoSymbol then s else root(s.owner)
val topLevelPck = root(tree.symbol)
rootDoc = Some(parseCommentString(using parser.qctx, summon[DocContext])(content, topLevelPck, None))
}
for tasty <- tastys do {
val root = tasty.ast
if !isSkipped(root.symbol) then
val treeRoot = root.asInstanceOf[parser.qctx.reflect.Tree]
processRootDocIfNeeded(treeRoot)
topLevels ++= parser.parseRootTree(treeRoot)
}
if ctx.args.documentSyntheticTypes then
import parser.qctx.reflect._
val intrinsicTypeDefs = parser.intrinsicTypeDefs.toSeq.map { s =>
"scala" -> parser.parseTypeDef(
s.tree.asInstanceOf[TypeDef],
defn.AnyClass.tree.asInstanceOf[ClassDef],
)
}
val intrinsicClassDefs = parser.intrinsicClassDefs.toSeq.map { s =>
"scala" -> parser.parseClasslike(s.tree.asInstanceOf[ClassDef])
}
topLevels ++= intrinsicClassDefs
topLevels ++= intrinsicTypeDefs
val scalaPckg = defn.ScalaPackage
given parser.qctx.type = parser.qctx
topLevels += "scala" -> Member(scalaPckg.fullName, "", scalaPckg.dri, Kind.Package)
topLevels += mergeAnyRefAliasAndObject(parser)
def mergeAnyRefAliasAndObject(parser: TastyParser) =
import parser.qctx.reflect._
val javaLangObjectDef = defn.ObjectClass.tree.asInstanceOf[ClassDef]
val objectMembers = parser.extractPatchedMembers(javaLangObjectDef)
val aM = parser.parseTypeDef(
defn.AnyRefClass.tree.asInstanceOf[TypeDef],
defn.AnyClass.tree.asInstanceOf[ClassDef],
)
"scala" -> aM.copy(
kind = Kind.Class(Nil, Nil),
members = objectMembers
)
object ScaladocTastyInspector:
def loadDocs()(using ctx: DocContext): (List[Member], Option[Comment]) =
val filePaths = ctx.args.tastyFiles.map(_.getAbsolutePath).toList
val classpath = ctx.args.classpath.split(java.io.File.pathSeparator).toList
val inspector = new ScaladocTastyInspector
val (tastyPaths, nonTastyPaths) = filePaths.partition(_.endsWith(".tasty"))
val (jarPaths, invalidPaths) = nonTastyPaths.partition(_.endsWith(".jar"))
for invalidPath <- invalidPaths do
report.error("File extension is not `tasty` or `jar`: " + invalidPath)
if tastyPaths.nonEmpty then
TastyInspector.inspectAllTastyFiles(tastyPaths, jarPaths, classpath)(inspector)
val all = inspector.topLevels.result()
all.groupBy(_._1).map { case (pckName, members) =>
val (pcks, rest) = members.map(_._2).partition(_.kind == Kind.Package)
val basePck = pcks.reduce( (p1, p2) =>
val withNewMembers = p1.withNewMembers(p2.members)
if withNewMembers.docs.isEmpty then withNewMembers.withDocs(p2.docs) else withNewMembers
)
basePck.withMembers((basePck.members ++ rest).sortBy(_.name))
}.toList -> inspector.rootDoc
end ScaladocTastyInspector
/** Parses a single Tasty compilation unit. */
case class TastyParser(
qctx: Quotes,
inspector: ScaladocTastyInspector,
)(
isSkipped: qctx.reflect.Symbol => Boolean
)(
using val ctx: DocContext
) extends BasicSupport with TypesSupport with ClassLikeSupport with PackageSupport with InkuireSupport:
import qctx.reflect._
private given qctx.type = qctx
val intrinsicClassDefs = Set(
defn.AnyClass,
defn.MatchableClass,
defn.ScalaPackage.typeMember("AnyKind"),
defn.AnyValClass,
defn.NullClass,
defn.NothingClass,
defn.ScalaPackage.typeMember("Singleton"),
)
val noPosClassDefs = intrinsicClassDefs ++ Set(
defn.ObjectClass,
defn.AnyRefClass
)
val intrinsicTypeDefs = Set(
defn.ScalaPackage.typeMember("&"),
defn.ScalaPackage.typeMember("|"),
)
def processTree[T](tree: Tree)(op: => T): Option[T] = try Option(op) catch
case e: Exception =>
report.warning(throwableToString(e), tree.pos)
None
def processTreeOpt[T](tree: Tree)(op: => Option[T]): Option[T] = try op catch
case e: Exception =>
report.warning(throwableToString(e), tree.pos)
None
def parseRootTree(root: Tree): Seq[(String, Member)] =
val docs = Seq.newBuilder[(String, Member)]
object Traverser extends TreeTraverser:
var seen: List[Tree] = Nil
override def traverseTree(tree: Tree)(owner: Symbol): Unit =
seen = tree :: seen
if !isSkipped(tree.symbol) then tree match {
case pck: PackageClause =>
docs += parsePackage(pck)
super.traverseTree(tree)(owner)
case packageObject: ClassDef if(packageObject.symbol.name.contains("package$")) =>
docs += parsePackageObject(packageObject)
case clazz: ClassDef if clazz.symbol.shouldDocumentClasslike =>
docs += clazz.symbol.packageName -> parseClasslike(clazz)
case _ =>
}
seen = seen.tail
try Traverser.traverseTree(root)(Symbol.spliceOwner)
catch case e: Throwable =>
println(s"Problem parsing ${root.pos}, documentation may not be generated.")
// e.printStackTrace()
docs.result()