Skip to content

Commit 7524970

Browse files
committed
Rework TastyInspector API to allow inspection of all files
1 parent 0ad0dc8 commit 7524970

File tree

10 files changed

+130
-105
lines changed

10 files changed

+130
-105
lines changed
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)(sources: List[String], trees: List[quotes.reflect.Tree]): Unit
24+
25+
end Inspector

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

Lines changed: 18 additions & 30 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,7 +49,7 @@ 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

6154
/** Load and process TASTy files using TASTy reflect and provided context
6255
*
@@ -65,31 +58,27 @@ trait TastyInspector:
6558
* @param classes List of paths of `.tasty` and `.jar` files (no validation is performed)
6659
* @param classpath Classpath with extra dependencies needed to load class in the `.tasty` files
6760
*/
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])
61+
// protected[inspector] def inspectFilesInContext(classpath: List[String], classes: List[String])(using Context): Unit =
62+
// if (classes.isEmpty) report.error("Parameter classes should no be empty")
63+
// inspectorDriver(inspector).process(inspectorArgs(classpath, classes), summon[Context])
7164

7265

73-
private def inspectorDriver() =
66+
private def inspectorDriver(inspector: Inspector) =
7467
class InspectorDriver extends Driver:
7568
override protected def newCompiler(implicit ctx: Context): Compiler = new TastyFromClass
7669

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

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-
8773
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
88-
val qctx = QuotesImpl()
89-
self.postProcess(using qctx)
74+
val quotesImpl = QuotesImpl()
75+
val sources = units.map(unit => unit.source.path)
76+
val trees = units.map(unit => unit.tpdTree.asInstanceOf[quotesImpl.reflect.Tree])
77+
inspector.inspect(using quotesImpl)(sources, trees)
9078
units
9179

9280
override def run(implicit ctx: Context): Unit = unsupported("run")
81+
end TastyInspectorPhase
9382

9483
class TastyFromClass extends TASTYCompiler:
9584

@@ -103,7 +92,6 @@ trait TastyInspector:
10392

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

10997
override def newRun(implicit ctx: Context): Run =
@@ -118,11 +106,11 @@ trait TastyInspector:
118106
("-from-tasty" :: "-Yretain-trees" :: "-classpath" :: fullClasspath :: classes).toArray
119107

120108

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

125-
val reporter = inspectorDriver().process(inspectorArgs(classpath, classes))
113+
val reporter = inspectorDriver(inspector).process(inspectorArgs(classpath, classes))
126114
reporter.hasErrors
127115

128116
end inspectFiles

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,20 @@ 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)(sources: List[String], trees: List[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 <- trees 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)
48+
}

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)(sources: List[String], trees: List[quotes.reflect.Tree]): Unit =
26+
for tree <- trees 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)(sources: List[String], trees: List[quotes.reflect.Tree]): Unit = {
7+
for tree <- trees 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
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ import scala.tasty.inspector._
99
val tastyFiles = allTastyFiles.filter(_.contains("TraitParams"))
1010

1111
// in dotty-example-project
12-
val inspector = new TastyInspector {
13-
protected def processCompilationUnit(using Quotes)(tree: quotes.reflect.Tree): Unit = {
14-
println(tree.show)
12+
val inspector = new Inspector {
13+
def inspect(using Quotes)(sources: List[String], trees: List[quotes.reflect.Tree]): Unit = {
14+
for tree <- trees do
15+
println(tree.show)
1516
}
1617
}
17-
inspector.inspectTastyFiles(tastyFiles)
18+
TastyInspector.inspectTastyFiles(tastyFiles)(inspector)
1819
}
1920

2021
object TraitParams {

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,25 @@ object Test {
2323

2424
// Tasty Scala Class
2525
val inspect1 = new TestInspector_Children()
26-
inspect1.inspectTastyFiles(allTastyFiles.filter(_.contains("Vehicle")))
26+
TastyInspector.inspectTastyFiles(allTastyFiles.filter(_.contains("Vehicle")))(inspect1)
2727
assert(inspect1.kids == List("Truck","Car","Plane"))
2828

2929
// Java Class
3030
val inspect2 = new TestInspector_Children()
31-
inspect2.inspectTastyFiles(allTastyFiles.filter(_.contains("Flavor")))
31+
TastyInspector.inspectTastyFiles(allTastyFiles.filter(_.contains("Flavor")))(inspect2)
3232
assert(inspect2.kids == List("Vanilla","Chocolate","Bourbon"))
3333
}
3434
}
3535

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

3838
var kids: List[String] = Nil
3939

40-
protected def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit =
40+
def inspect(using Quotes)(sources: List[String], trees: List[quotes.reflect.Tree]): Unit = {
4141
import quotes.reflect._
42-
inspectClass(root)
42+
for tree <- trees do
43+
inspectClass(tree)
44+
}
4345

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

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

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -40,50 +40,52 @@ object Test {
4040
val allTastyFiles = dotty.tools.io.Path(classpath).walkFilter(_.extension == "tasty").map(_.toString).toList
4141
val tastyFiles = allTastyFiles.filter(_.contains("I9970"))
4242

43-
new TestInspector().inspectTastyFiles(tastyFiles)
43+
TastyInspector.inspectTastyFiles(tastyFiles)(new TestInspector())
4444
}
4545
}
4646

4747
// Inspector that performs the actual tests
4848

49-
class TestInspector() extends TastyInspector:
50-
51-
private var foundIOApp: Boolean = false
52-
private var foundSimple: Boolean = false
53-
54-
protected def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit =
55-
foundIOApp = false
56-
foundSimple = false
57-
inspectClass(root)
58-
// Sanity check to make sure that our pattern matches are not simply glossing over the things we want to test
59-
assert(foundIOApp, "the inspector did not encounter IOApp")
60-
assert(foundSimple, "the inspect did not encounter IOApp.Simple")
61-
62-
private def inspectClass(using Quotes)(tree: quotes.reflect.Tree): Unit =
63-
import quotes.reflect._
64-
tree match
65-
case t: PackageClause =>
66-
t.stats.foreach(inspectClass(_))
67-
68-
case t: ClassDef if t.name.endsWith("$") =>
69-
t.body.foreach(inspectClass(_))
70-
71-
case t: ClassDef =>
72-
t.name match
73-
case "I9970IOApp" =>
74-
foundIOApp = true
75-
// Cannot test the following because NoInits is not part of the quotes API
76-
//assert(!t.symbol.flags.is(Flags.NoInits))
77-
assert(!t.constructor.symbol.flags.is(Flags.StableRealizable))
78-
79-
case "Simple" =>
80-
foundSimple = true
81-
// Cannot test the following because NoInits is not part of the quotes API
82-
//assert(t.symbol.flags.is(Flags.NoInits))
83-
assert(t.constructor.symbol.flags.is(Flags.StableRealizable))
84-
85-
case _ =>
86-
assert(false, s"unexpected ClassDef '${t.name}'")
87-
88-
case _ =>
89-
end inspectClass
49+
class TestInspector() extends Inspector:
50+
51+
def inspect(using Quotes)(sources: List[String], trees: List[quotes.reflect.Tree]): Unit =
52+
var foundIOApp: Boolean = false
53+
var foundSimple: Boolean = false
54+
55+
def inspectClass(using Quotes)(tree: quotes.reflect.Tree): Unit =
56+
import quotes.reflect._
57+
tree match
58+
case t: PackageClause =>
59+
t.stats.foreach(inspectClass(_))
60+
61+
case t: ClassDef if t.name.endsWith("$") =>
62+
t.body.foreach(inspectClass(_))
63+
64+
case t: ClassDef =>
65+
t.name match
66+
case "I9970IOApp" =>
67+
foundIOApp = true
68+
// Cannot test the following because NoInits is not part of the quotes API
69+
//assert(!t.symbol.flags.is(Flags.NoInits))
70+
assert(!t.constructor.symbol.flags.is(Flags.StableRealizable))
71+
72+
case "Simple" =>
73+
foundSimple = true
74+
// Cannot test the following because NoInits is not part of the quotes API
75+
//assert(t.symbol.flags.is(Flags.NoInits))
76+
assert(t.constructor.symbol.flags.is(Flags.StableRealizable))
77+
78+
case _ =>
79+
assert(false, s"unexpected ClassDef '${t.name}'")
80+
81+
case _ =>
82+
83+
for tree <- trees do
84+
foundIOApp = false
85+
foundSimple = false
86+
inspectClass(tree)
87+
// Sanity check to make sure that our pattern matches are not simply glossing over the things we want to test
88+
assert(foundIOApp, "the inspector did not encounter IOApp")
89+
assert(foundSimple, "the inspect did not encounter IOApp.Simple")
90+
91+
end inspect

tests/run-custom-args/tasty-inspector/tasty-documentation-inspector/Test.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ object Test {
99
val allTastyFiles = dotty.tools.io.Path(classpath).walkFilter(_.extension == "tasty").map(_.toString).toList
1010
val tastyFiles = allTastyFiles.filter(_.contains("Foo"))
1111

12-
new DocumentationInspector().inspectTastyFiles(tastyFiles)
12+
TastyInspector.inspectTastyFiles(tastyFiles)(new DocumentationInspector())
1313
}
1414
}
1515

16-
class DocumentationInspector extends TastyInspector {
16+
class DocumentationInspector extends Inspector {
17+
18+
def inspect(using Quotes)(sources: List[String], trees: List[quotes.reflect.Tree]): Unit = {
1719

18-
protected def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit = {
1920
import quotes.reflect._
2021
object Traverser extends TreeTraverser {
2122

@@ -31,7 +32,8 @@ class DocumentationInspector extends TastyInspector {
3132
}
3233

3334
}
34-
Traverser.traverseTree(root)
35+
for tree <- trees do
36+
Traverser.traverseTree(tree)
3537
}
3638

3739
}

0 commit comments

Comments
 (0)