Skip to content

Commit

Permalink
Remove OldTastyInspector from scaladoc (scala#17623)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed May 31, 2023
2 parents 28915c4 + f36c302 commit d0b790e
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ case class Module(rootPackage: Member, members: Map[DRI, Member])

object ScalaModuleProvider:
def mkModule()(using ctx: DocContext): Module =
val (result, rootDoc) = ScaladocTastyInspector().result()
val (result, rootDoc) = ScaladocTastyInspector.loadDocs()
val (rootPck, rest) = result.partition(_.name == "API")
val (emptyPackages, nonemptyPackages) = (rest ++ rootPck.flatMap(_.members))
.filter(p => p.members.nonEmpty || p.docs.nonEmpty).sortBy(_.name)
Expand Down
65 changes: 34 additions & 31 deletions scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package tasty
import java.util.regex.Pattern

import scala.util.{Try, Success, Failure}
import scala.tasty.inspector.DocTastyInspector
import scala.tasty.inspector.{TastyInspector, Inspector, Tasty}
import scala.quoted._

import dotty.tools.dotc
Expand All @@ -24,24 +24,12 @@ import ScaladocSupport._
*
* Delegates most of the work to [[TastyParser]] [[dotty.tools.scaladoc.tasty.TastyParser]].
*/
case class ScaladocTastyInspector()(using ctx: DocContext) extends DocTastyInspector:
case class ScaladocTastyInspector()(using ctx: DocContext) extends Inspector:

private val topLevels = Seq.newBuilder[(String, Member)]
private var rootDoc: Option[Comment] = None

def processCompilationUnit(using Quotes)(root: reflect.Tree): Unit = ()

override def postProcess(using Quotes): Unit =
// hack into the compiler to get a list of all top-level trees
// in principle, to do this, one would collect trees in processCompilationUnit
// however, path-dependent types disallow doing so w/o using casts
inline def hackForeachTree(thunk: reflect.Tree => Unit): Unit =
given dctx: dotc.core.Contexts.Context = quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
dctx.run.nn.units.foreach { compilationUnit =>
// mirrors code from TastyInspector
thunk(compilationUnit.tpdTree.asInstanceOf[reflect.Tree])
}

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
Expand Down Expand Up @@ -116,7 +104,8 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends DocTastyInspe
rootDoc = Some(parseCommentString(using parser.qctx, summon[DocContext])(content, topLevelPck, None))
}

hackForeachTree { root =>
for tasty <- tastys do {
val root = tasty.ast
if !isSkipped(root.symbol) then
val treeRoot = root.asInstanceOf[parser.qctx.reflect.Tree]
processRootDocIfNeeded(treeRoot)
Expand All @@ -138,33 +127,47 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends DocTastyInspe
topLevels += "scala" -> Member(scalaPckg.fullName, "", scalaPckg.dri, Kind.Package)
topLevels += mergeAnyRefAliasAndObject(parser)

def result(): (List[Member], Option[Comment]) =
topLevels.clear()
rootDoc = None


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])
"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

if filePaths.nonEmpty then inspectFilesInContext(classpath, filePaths)
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 = topLevels.result()
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 -> rootDoc
}.toList -> inspector.rootDoc

end ScaladocTastyInspector

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])
"scala" -> aM.copy(
kind = Kind.Class(Nil, Nil),
members = objectMembers
)
/** Parses a single Tasty compilation unit. */
case class TastyParser(
qctx: Quotes,
Expand Down
9 changes: 0 additions & 9 deletions scaladoc/src/scala/tasty/inspector/DocTastyInspector.scala

This file was deleted.

33 changes: 33 additions & 0 deletions scaladoc/src/scala/tasty/inspector/Inspector.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copy of tasty-inspector/src/scala/tasty/inspector/Inspector.scala
// FIXME remove this copy of the file

package scala.tasty.inspector

import scala.quoted._
import scala.quoted.runtime.impl.QuotesImpl

import dotty.tools.dotc.Compiler
import dotty.tools.dotc.Driver
import dotty.tools.dotc.Run
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Mode
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.fromtasty._
import dotty.tools.dotc.util.ClasspathFromClassloader
import dotty.tools.dotc.CompilationUnit
import dotty.tools.unsupported
import dotty.tools.dotc.report

import java.io.File.pathSeparator

trait Inspector:

/** Inspect all TASTy files using `Quotes` reflect API.
*
* Note: Within this method `quotes.reflect.SourceFile.current` will not work, hence the explicit source paths.
*
* @param tastys List of `Tasty` containing `.tasty`file path and AST
*/
def inspect(using Quotes)(tastys: List[Tasty[quotes.type]]): Unit

end Inspector
20 changes: 20 additions & 0 deletions scaladoc/src/scala/tasty/inspector/Tasty.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copy of tasty-inspector/src/scala/tasty/inspector/Tasty.scala
// FIXME remove this copy of the file

package scala.tasty.inspector

import scala.quoted._

/** `.tasty` file representation containing file path and the AST */
trait Tasty[Q <: Quotes & Singleton]:

/** Instance of `Quotes` used to load the AST */
val quotes: Q

/** Path to the `.tasty` file */
def path: String

/** Abstract Syntax Tree contained in the `.tasty` file */
def ast: quotes.reflect.Tree

end Tasty
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copy of tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala
// FIXME remove this copy of the file

package scala.tasty.inspector

import scala.quoted._
Expand All @@ -10,45 +13,43 @@ import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Mode
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.fromtasty._
import dotty.tools.dotc.quoted.QuotesCache
import dotty.tools.dotc.util.ClasspathFromClassloader
import dotty.tools.dotc.CompilationUnit
import dotty.tools.unsupported
import dotty.tools.dotc.report

import java.io.File.pathSeparator

// COPY OF OLD IMPLEMENTATION
// TODO: update to new implementation
trait OldTastyInspector:
self =>

/** Process a TASTy file using TASTy reflect */
protected def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit

/** Called after all compilation units are processed */
protected def postProcess(using Quotes): Unit = ()
object TastyInspector:

/** Load and process TASTy files using TASTy reflect
*
* @param tastyFiles List of paths of `.tasty` files
*
* @return boolean value indicating whether the process succeeded
*/
def inspectTastyFiles(tastyFiles: List[String]): Boolean =
inspectAllTastyFiles(tastyFiles, Nil, Nil)
def inspectTastyFiles(tastyFiles: List[String])(inspector: Inspector): Boolean =
inspectAllTastyFiles(tastyFiles, Nil, Nil)(inspector)

/** Load and process TASTy files in a `jar` file using TASTy reflect
*
* @param jars Path of `.jar` file
*
* @return boolean value indicating whether the process succeeded
*/
def inspectTastyFilesInJar(jar: String): Boolean =
inspectAllTastyFiles(Nil, List(jar), Nil)
def inspectTastyFilesInJar(jar: String)(inspector: Inspector): Boolean =
inspectAllTastyFiles(Nil, List(jar), Nil)(inspector)

/** Load and process TASTy files using TASTy reflect
*
* @param tastyFiles List of paths of `.tasty` files
* @param jars List of path of `.jar` files
* @param dependenciesClasspath Classpath with extra dependencies needed to load class in the `.tasty` files
*
* @return boolean value indicating whether the process succeeded
*/
def inspectAllTastyFiles(tastyFiles: List[String], jars: List[String], dependenciesClasspath: List[String]): Boolean =
def inspectAllTastyFiles(tastyFiles: List[String], jars: List[String], dependenciesClasspath: List[String])(inspector: Inspector): Boolean =
def checkFile(fileName: String, ext: String): Unit =
val file = dotty.tools.io.Path(fileName)
if file.extension != ext then
Expand All @@ -58,40 +59,30 @@ trait OldTastyInspector:
tastyFiles.foreach(checkFile(_, "tasty"))
jars.foreach(checkFile(_, "jar"))
val files = tastyFiles ::: jars
files.nonEmpty && inspectFiles(dependenciesClasspath, files)

/** Load and process TASTy files using TASTy reflect and provided context
*
* Used in doctool to reuse reporter and setup provided by sbt
*
* @param classes List of paths of `.tasty` and `.jar` files (no validation is performed)
* @param classpath Classpath with extra dependencies needed to load class in the `.tasty` files
*/
protected[inspector] def inspectFilesInContext(classpath: List[String], classes: List[String])(using Context): Unit =
if (classes.isEmpty) report.error("Parameter classes should no be empty")
inspectorDriver().process(inspectorArgs(classpath, classes), summon[Context])

inspectFiles(dependenciesClasspath, files)(inspector)

private def inspectorDriver() =
private def inspectorDriver(inspector: Inspector) =
class InspectorDriver extends Driver:
override protected def newCompiler(implicit ctx: Context): Compiler = new TastyFromClass

class TastyInspectorPhase extends Phase:
override def phaseName: String = "tastyInspector"

override def run(implicit ctx: Context): Unit =
val qctx = QuotesImpl()
self.processCompilationUnit(using qctx)(ctx.compilationUnit.tpdTree.asInstanceOf[qctx.reflect.Tree])

class TastyInspectorFinishPhase extends Phase:
override def phaseName: String = "tastyInspectorFinish"

override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
val qctx = QuotesImpl()
self.postProcess(using qctx)
override def runOn(units: List[CompilationUnit])(using ctx0: Context): List[CompilationUnit] =
val ctx = QuotesCache.init(ctx0.fresh)
runOnImpl(units)(using ctx)

private def runOnImpl(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
val quotesImpl = QuotesImpl()
class TastyImpl(val path: String, val ast: quotesImpl.reflect.Tree) extends Tasty[quotesImpl.type] {
val quotes = quotesImpl
}
val tastys = units.map(unit => new TastyImpl(unit.source.path , unit.tpdTree.asInstanceOf[quotesImpl.reflect.Tree]))
inspector.inspect(using quotesImpl)(tastys)
units

override def run(implicit ctx: Context): Unit = unsupported("run")
end TastyInspectorPhase

class TastyFromClass extends TASTYCompiler:

Expand All @@ -105,7 +96,6 @@ trait OldTastyInspector:

override protected def backendPhases: List[List[Phase]] =
List(new TastyInspectorPhase) :: // Perform a callback for each compilation unit
List(new TastyInspectorFinishPhase) :: // Perform a final callback
Nil

override def newRun(implicit ctx: Context): Run =
Expand All @@ -123,14 +113,14 @@ trait OldTastyInspector:
("-from-tasty" :: "-Yretain-trees" :: "-classpath" :: fullClasspath :: classes).toArray


private def inspectFiles(classpath: List[String], classes: List[String]): Boolean =
if (classes.isEmpty)
throw new IllegalArgumentException("Parameter classes should no be empty")

val reporter = inspectorDriver().process(inspectorArgs(classpath, classes))
reporter.hasErrors
private def inspectFiles(classpath: List[String], classes: List[String])(inspector: Inspector): Boolean =
classes match
case Nil => true
case _ =>
val reporter = inspectorDriver(inspector).process(inspectorArgs(classpath, classes))
!reporter.hasErrors

end inspectFiles


end OldTastyInspector
end TastyInspector
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package dotty.tools.scaladoc
package tasty.comments

import scala.quoted.Quotes
import scala.quoted.*

import org.junit.{Test, Rule}
import org.junit.Assert.{assertSame, assertTrue}
Expand Down Expand Up @@ -198,14 +198,11 @@ class MemberLookupTests {

@Test
def test(): Unit = {
import scala.tasty.inspector.OldTastyInspector
class Inspector extends OldTastyInspector:
var alreadyRan: Boolean = false
import scala.tasty.inspector.*
class MyInspector extends Inspector:

override def processCompilationUnit(using ctx: quoted.Quotes)(root: ctx.reflect.Tree): Unit =
if !alreadyRan then
this.test()
alreadyRan = true
def inspect(using Quotes)(tastys: List[Tasty[quotes.type]]): Unit =
this.test()

def test()(using q: Quotes): Unit = {
import dotty.tools.scaladoc.tasty.comments.MemberLookup
Expand All @@ -215,6 +212,6 @@ class MemberLookupTests {
cases.testAll()
}

Inspector().inspectTastyFiles(TestUtils.listOurClasses())
TastyInspector.inspectTastyFiles(TestUtils.listOurClasses())(new MyInspector)
}
}

0 comments on commit d0b790e

Please sign in to comment.