Skip to content

Commit 5e32f93

Browse files
committed
Rework TastyInspector API to allow inspection of all files
1 parent 65b17af commit 5e32f93

File tree

17 files changed

+282
-133
lines changed

17 files changed

+282
-133
lines changed

scala3doc/src/scala/tasty/inspector/DocTastyInspector.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package scala.tasty.inspector
22

33
import dotty.tools.dotc.core.Contexts.Context
44

5-
abstract class DocTastyInspector extends TastyInspector:
5+
abstract class DocTastyInspector extends OldTastyInspector:
66
def inspectFilesInDocContext(
77
classpath: List[String],
88
filePaths: List[String])(
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package scala.tasty.inspector
2+
3+
import scala.quoted._
4+
import scala.quoted.runtime.impl.QuotesImpl
5+
6+
import dotty.tools.dotc.Compiler
7+
import dotty.tools.dotc.Driver
8+
import dotty.tools.dotc.Run
9+
import dotty.tools.dotc.core.Contexts.Context
10+
import dotty.tools.dotc.core.Mode
11+
import dotty.tools.dotc.core.Phases.Phase
12+
import dotty.tools.dotc.fromtasty._
13+
import dotty.tools.dotc.util.ClasspathFromClassloader
14+
import dotty.tools.dotc.CompilationUnit
15+
import dotty.tools.unsupported
16+
import dotty.tools.dotc.report
17+
18+
import java.io.File.pathSeparator
19+
20+
// COPY OF OLD IMPLEMENTATION
21+
// TODO: update to new implementation
22+
trait OldTastyInspector:
23+
self =>
24+
25+
/** Process a TASTy file using TASTy reflect */
26+
protected def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit
27+
28+
/** Called after all compilation units are processed */
29+
protected def postProcess(using Quotes): Unit = ()
30+
31+
/** Load and process TASTy files using TASTy reflect
32+
*
33+
* @param tastyFiles List of paths of `.tasty` files
34+
*/
35+
def inspectTastyFiles(tastyFiles: List[String]): Boolean =
36+
inspectAllTastyFiles(tastyFiles, Nil, Nil)
37+
38+
/** Load and process TASTy files in a `jar` file using TASTy reflect
39+
*
40+
* @param jars Path of `.jar` file
41+
*/
42+
def inspectTastyFilesInJar(jar: String): Boolean =
43+
inspectAllTastyFiles(Nil, List(jar), Nil)
44+
45+
/** Load and process TASTy files using TASTy reflect
46+
*
47+
* @param tastyFiles List of paths of `.tasty` files
48+
* @param jars List of path of `.jar` files
49+
* @param dependenciesClasspath Classpath with extra dependencies needed to load class in the `.tasty` files
50+
*/
51+
def inspectAllTastyFiles(tastyFiles: List[String], jars: List[String], dependenciesClasspath: List[String]): Boolean =
52+
def checkFile(fileName: String, ext: String): Unit =
53+
val file = dotty.tools.io.Path(fileName)
54+
if file.extension != ext then
55+
throw new IllegalArgumentException(s"File extension is not `.$ext`: $file")
56+
else if !file.exists then
57+
throw new IllegalArgumentException(s"File not found: ${file.toAbsolute}")
58+
tastyFiles.foreach(checkFile(_, "tasty"))
59+
jars.foreach(checkFile(_, "jar"))
60+
val files = tastyFiles ::: jars
61+
files.nonEmpty && inspectFiles(dependenciesClasspath, files)
62+
63+
/** Load and process TASTy files using TASTy reflect and provided context
64+
*
65+
* Used in doctool to reuse reporter and setup provided by sbt
66+
*
67+
* @param classes List of paths of `.tasty` and `.jar` files (no validation is performed)
68+
* @param classpath Classpath with extra dependencies needed to load class in the `.tasty` files
69+
*/
70+
protected[inspector] def inspectFilesInContext(classpath: List[String], classes: List[String])(using Context): Unit =
71+
if (classes.isEmpty) report.error("Parameter classes should no be empty")
72+
inspectorDriver().process(inspectorArgs(classpath, classes), summon[Context])
73+
74+
75+
private def inspectorDriver() =
76+
class InspectorDriver extends Driver:
77+
override protected def newCompiler(implicit ctx: Context): Compiler = new TastyFromClass
78+
79+
class TastyInspectorPhase extends Phase:
80+
override def phaseName: String = "tastyInspector"
81+
82+
override def run(implicit ctx: Context): Unit =
83+
val qctx = QuotesImpl()
84+
self.processCompilationUnit(using qctx)(ctx.compilationUnit.tpdTree.asInstanceOf[qctx.reflect.Tree])
85+
86+
class TastyInspectorFinishPhase extends Phase:
87+
override def phaseName: String = "tastyInspectorFinish"
88+
89+
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
90+
val qctx = QuotesImpl()
91+
self.postProcess(using qctx)
92+
units
93+
94+
override def run(implicit ctx: Context): Unit = unsupported("run")
95+
96+
class TastyFromClass extends TASTYCompiler:
97+
98+
override protected def frontendPhases: List[List[Phase]] =
99+
List(new ReadTasty) :: // Load classes from tasty
100+
Nil
101+
102+
override protected def picklerPhases: List[List[Phase]] = Nil
103+
104+
override protected def transformPhases: List[List[Phase]] = Nil
105+
106+
override protected def backendPhases: List[List[Phase]] =
107+
List(new TastyInspectorPhase) :: // Perform a callback for each compilation unit
108+
List(new TastyInspectorFinishPhase) :: // Perform a final callback
109+
Nil
110+
111+
override def newRun(implicit ctx: Context): Run =
112+
reset()
113+
new TASTYRun(this, ctx.fresh.addMode(Mode.ReadPositions).addMode(Mode.ReadComments))
114+
115+
new InspectorDriver
116+
117+
private def inspectorArgs(classpath: List[String], classes: List[String]): Array[String] =
118+
val currentClasspath = ClasspathFromClassloader(getClass.getClassLoader)
119+
val fullClasspath = (classpath :+ currentClasspath).mkString(pathSeparator)
120+
("-from-tasty" :: "-Yretain-trees" :: "-classpath" :: fullClasspath :: classes).toArray
121+
122+
123+
private def inspectFiles(classpath: List[String], classes: List[String]): Boolean =
124+
if (classes.isEmpty)
125+
throw new IllegalArgumentException("Parameter classes should no be empty")
126+
127+
val reporter = inspectorDriver().process(inspectorArgs(classpath, classes))
128+
reporter.hasErrors
129+
130+
end inspectFiles
131+
132+
133+
end OldTastyInspector

scala3doc/test/dotty/dokka/tasty/comments/CommentExpanderTests.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ class CommentExpanderTests {
3737

3838
@Test
3939
def test(): Unit = {
40-
import scala.tasty.inspector.TastyInspector
41-
class Inspector extends TastyInspector:
40+
import scala.tasty.inspector.OldTastyInspector
41+
class Inspector extends OldTastyInspector:
4242

4343
def processCompilationUnit(using quoted.Quotes)(root: quotes.reflect.Tree): Unit = ()
4444

scala3doc/test/dotty/dokka/tasty/comments/MemberLookupTests.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ class MemberLookupTests {
101101

102102
@Test
103103
def test(): Unit = {
104-
import scala.tasty.inspector.TastyInspector
105-
class Inspector extends TastyInspector:
104+
import scala.tasty.inspector.OldTastyInspector
105+
class Inspector extends OldTastyInspector:
106106
var alreadyRan: Boolean = false
107107

108108
override def processCompilationUnit(using ctx: quoted.Quotes)(root: ctx.reflect.Tree): Unit =

stdlib-bootstrapped-tasty-tests/test/BootstrappedStdLibTASYyTest.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dotty.tools.io._
88
import dotty.tools.dotc.util.ClasspathFromClassloader
99

1010
import scala.quoted._
11+
import scala.tasty.inspector._
1112

1213
import java.io.File.pathSeparator
1314
import java.io.File.separator
@@ -101,13 +102,14 @@ object BootstrappedStdLibTASYyTest:
101102
.toList
102103

103104
def loadWithTastyInspector(blacklisted: Set[String]): Unit =
104-
val inspector = new scala.tasty.inspector.TastyInspector {
105-
def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit =
106-
root.show(using quotes.reflect.Printer.TreeStructure) // Check that we can traverse the full tree
105+
val inspector = new scala.tasty.inspector.Inspector {
106+
def inspect(using Quotes)(tastys: List[(String, quotes.reflect.Tree)]): Unit =
107+
for (_, tree) <- tastys do
108+
tree.show(using quotes.reflect.Printer.TreeStructure) // Check that we can traverse the full tree
107109
()
108110
}
109111
val tastyFiles = scalaLibTastyPaths.filterNot(blacklisted)
110-
val hasErrors = inspector.inspectTastyFiles(tastyFiles.map(x => scalaLibClassesPath.resolve(x).toString))
112+
val hasErrors = TastyInspector.inspectTastyFiles(tastyFiles.map(x => scalaLibClassesPath.resolve(x).toString))(inspector)
111113
assert(!hasErrors, "Errors reported while loading from TASTy")
112114

113115
def compileFromTastyInJar(blacklisted: Set[String]): Unit = {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package scala.tasty.inspector
2+
3+
import scala.quoted._
4+
import scala.quoted.runtime.impl.QuotesImpl
5+
6+
import dotty.tools.dotc.Compiler
7+
import dotty.tools.dotc.Driver
8+
import dotty.tools.dotc.Run
9+
import dotty.tools.dotc.core.Contexts.Context
10+
import dotty.tools.dotc.core.Mode
11+
import dotty.tools.dotc.core.Phases.Phase
12+
import dotty.tools.dotc.fromtasty._
13+
import dotty.tools.dotc.util.ClasspathFromClassloader
14+
import dotty.tools.dotc.CompilationUnit
15+
import dotty.tools.unsupported
16+
import dotty.tools.dotc.report
17+
18+
import java.io.File.pathSeparator
19+
20+
trait Inspector:
21+
22+
/** Inspect all TASTy files using `Quotes` reflect API */
23+
def inspect(using Quotes)(tastys: List[(String, quotes.reflect.Tree)]): Unit
24+
25+
end Inspector

tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala

Lines changed: 14 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,36 +17,29 @@ import dotty.tools.dotc.report
1717

1818
import java.io.File.pathSeparator
1919

20-
trait TastyInspector:
21-
self =>
22-
23-
/** Process a TASTy file using TASTy reflect */
24-
protected def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit
25-
26-
/** Called after all compilation units are processed */
27-
protected def postProcess(using Quotes): Unit = ()
20+
object TastyInspector:
2821

2922
/** Load and process TASTy files using TASTy reflect
3023
*
3124
* @param tastyFiles List of paths of `.tasty` files
3225
*/
33-
def inspectTastyFiles(tastyFiles: List[String]): Boolean =
34-
inspectAllTastyFiles(tastyFiles, Nil, Nil)
26+
def inspectTastyFiles(tastyFiles: List[String])(inspector: Inspector): Boolean =
27+
inspectAllTastyFiles(tastyFiles, Nil, Nil)(inspector)
3528

3629
/** Load and process TASTy files in a `jar` file using TASTy reflect
3730
*
3831
* @param jars Path of `.jar` file
3932
*/
40-
def inspectTastyFilesInJar(jar: String): Boolean =
41-
inspectAllTastyFiles(Nil, List(jar), Nil)
33+
def inspectTastyFilesInJar(jar: String)(inspector: Inspector): Boolean =
34+
inspectAllTastyFiles(Nil, List(jar), Nil)(inspector)
4235

4336
/** Load and process TASTy files using TASTy reflect
4437
*
4538
* @param tastyFiles List of paths of `.tasty` files
4639
* @param jars List of path of `.jar` files
4740
* @param dependenciesClasspath Classpath with extra dependencies needed to load class in the `.tasty` files
4841
*/
49-
def inspectAllTastyFiles(tastyFiles: List[String], jars: List[String], dependenciesClasspath: List[String]): Boolean =
42+
def inspectAllTastyFiles(tastyFiles: List[String], jars: List[String], dependenciesClasspath: List[String])(inspector: Inspector): Boolean =
5043
def checkFile(fileName: String, ext: String): Unit =
5144
val file = dotty.tools.io.Path(fileName)
5245
if file.extension != ext then
@@ -56,40 +49,23 @@ trait TastyInspector:
5649
tastyFiles.foreach(checkFile(_, "tasty"))
5750
jars.foreach(checkFile(_, "jar"))
5851
val files = tastyFiles ::: jars
59-
files.nonEmpty && inspectFiles(dependenciesClasspath, files)
52+
files.nonEmpty && inspectFiles(dependenciesClasspath, files)(inspector)
6053

61-
/** Load and process TASTy files using TASTy reflect and provided context
62-
*
63-
* Used in doctool to reuse reporter and setup provided by sbt
64-
*
65-
* @param classes List of paths of `.tasty` and `.jar` files (no validation is performed)
66-
* @param classpath Classpath with extra dependencies needed to load class in the `.tasty` files
67-
*/
68-
protected[inspector] def inspectFilesInContext(classpath: List[String], classes: List[String])(using Context): Unit =
69-
if (classes.isEmpty) report.error("Parameter classes should no be empty")
70-
inspectorDriver().process(inspectorArgs(classpath, classes), summon[Context])
71-
72-
73-
private def inspectorDriver() =
54+
private def inspectorDriver(inspector: Inspector) =
7455
class InspectorDriver extends Driver:
7556
override protected def newCompiler(implicit ctx: Context): Compiler = new TastyFromClass
7657

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

80-
override def run(implicit ctx: Context): Unit =
81-
val qctx = QuotesImpl()
82-
self.processCompilationUnit(using qctx)(ctx.compilationUnit.tpdTree.asInstanceOf[qctx.reflect.Tree])
83-
84-
class TastyInspectorFinishPhase extends Phase:
85-
override def phaseName: String = "tastyInspectorFinish"
86-
8761
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
88-
val qctx = QuotesImpl()
89-
self.postProcess(using qctx)
62+
val quotesImpl = QuotesImpl()
63+
val tastys = units.map(unit => (unit.source.path , unit.tpdTree.asInstanceOf[quotesImpl.reflect.Tree]))
64+
inspector.inspect(using quotesImpl)(tastys)
9065
units
9166

9267
override def run(implicit ctx: Context): Unit = unsupported("run")
68+
end TastyInspectorPhase
9369

9470
class TastyFromClass extends TASTYCompiler:
9571

@@ -103,7 +79,6 @@ trait TastyInspector:
10379

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

10984
override def newRun(implicit ctx: Context): Run =
@@ -118,11 +93,11 @@ trait TastyInspector:
11893
("-from-tasty" :: "-Yretain-trees" :: "-classpath" :: fullClasspath :: classes).toArray
11994

12095

121-
private def inspectFiles(classpath: List[String], classes: List[String]): Boolean =
96+
private def inspectFiles(classpath: List[String], classes: List[String])(inspector: Inspector): Boolean =
12297
if (classes.isEmpty)
12398
throw new IllegalArgumentException("Parameter classes should no be empty")
12499

125-
val reporter = inspectorDriver().process(inspectorArgs(classpath, classes))
100+
val reporter = inspectorDriver(inspector).process(inspectorArgs(classpath, classes))
126101
reporter.hasErrors
127102

128103
end inspectFiles

tests/run-custom-args/tasty-inspector/i10359.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,19 @@ object Test {
2929
val tastyFiles = allTastyFiles.filter(_.contains("TraitParams"))
3030

3131
val inspect = new TestInspector()
32-
inspect.inspectTastyFiles(allTastyFiles.filter(_.contains("Bar")))
32+
TastyInspector.inspectTastyFiles(allTastyFiles.filter(_.contains("Bar")))(inspect)
3333
}
3434
}
3535

36-
class TestInspector() extends TastyInspector:
36+
class TestInspector() extends Inspector:
3737

38-
protected def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit =
38+
def inspect(using Quotes)(tastys: List[(String, quotes.reflect.Tree)]): Unit =
3939
import quotes.reflect._
4040

41-
val code = root.show
42-
assert(code.contains("import Foo.this.g.{given}"), code)
43-
assert(code.contains("import Foo.this.g.{given scala.Int}"), code)
41+
for (_, root) <- tastys do
42+
val code = root.show
43+
assert(code.contains("import Foo.this.g.{given}"), code)
44+
assert(code.contains("import Foo.this.g.{given scala.Int}"), code)
4445

45-
val extractors = root.show(using Printer.TreeStructure)
46-
assert(extractors.contains("GivenSelector"), extractors)
46+
val extractors = root.show(using Printer.TreeStructure)
47+
assert(extractors.contains("GivenSelector"), extractors)

tests/run-custom-args/tasty-inspector/i8163.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ object Test {
1616
val allTastyFiles = dotty.tools.io.Path(classpath).walkFilter(_.extension == "tasty").map(_.toString).toList
1717
val tastyFiles = allTastyFiles.filter(_.contains("I8163"))
1818

19-
new TestInspector().inspectTastyFiles(tastyFiles)
19+
TastyInspector.inspectTastyFiles(tastyFiles)(new TestInspector())
2020
}
2121
}
2222

23-
class TestInspector() extends TastyInspector:
23+
class TestInspector() extends Inspector:
2424

25-
protected def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit =
26-
inspectClass(root)
25+
def inspect(using Quotes)(tastys: List[(String, quotes.reflect.Tree)]): Unit =
26+
for (_, tree) <- tastys do
27+
inspectClass(tree)
2728

2829
private def inspectClass(using Quotes)(tree: quotes.reflect.Tree): Unit =
2930
import quotes.reflect._

tests/run-custom-args/tasty-inspector/i8364.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@ import scala.quoted._
22
import scala.tasty.inspector._
33

44
@main def Test = {
5-
val inspector = new TastyInspector {
6-
protected def processCompilationUnit(using Quotes)(tree: quotes.reflect.Tree): Unit = {
7-
tree.show(using quotes.reflect.Printer.TreeStructure) // Make sure that tree is loaded and can be traveresed
5+
val inspector = new Inspector {
6+
def inspect(using Quotes)(tastys: List[(String, quotes.reflect.Tree)]): Unit = {
7+
for (_, tree) <- tastys do
8+
tree.show(using quotes.reflect.Printer.TreeStructure) // Make sure that tree is loaded and can be traveresed
89
}
910
}
1011

1112
// Artefact of the current test infrastructure
1213
// TODO improve infrastructure to avoid needing this code on each test
1314
val libJarClasspath = dotty.tools.dotc.util.ClasspathFromClassloader(this.getClass.getClassLoader).split(java.io.File.pathSeparator).find(x => x.contains("scala3-library-bootstrapped") && x.endsWith(".jar")).get
1415

15-
inspector.inspectTastyFilesInJar(libJarClasspath)
16+
TastyInspector.inspectTastyFilesInJar(libJarClasspath)(inspector)
1617
}

0 commit comments

Comments
 (0)