diff --git a/compiler/src/dotty/tools/dotc/fromtasty/JavaCompilationUnit.scala b/compiler/src/dotty/tools/dotc/fromtasty/JavaCompilationUnit.scala new file mode 100644 index 000000000000..de84b5c38561 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/fromtasty/JavaCompilationUnit.scala @@ -0,0 +1,12 @@ +package dotty.tools.dotc.fromtasty + +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.util.NoSource + +/** A marker CompilationUnit to return up the call stack from ReadTasty. This will tell us that we've + * encountered, and attempted to inspect, a Java class file. We can't TASTy-inspect a Java class obviously, + * but we want to return the fact we found it so that higher-up we can take appropriate action if desired. + */ +class JavaCompilationUnit(val className: String) extends CompilationUnit(NoSource) { + override def toString: String = s"Java class file $className" +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala b/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala index 837270504132..a909e12dcc2d 100644 --- a/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala +++ b/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala @@ -48,7 +48,11 @@ class ReadTasty extends Phase { Some(unit) } case tree: Tree[?] => - alreadyLoaded() + cls.denot.infoOrCompleter match { + case _: NoLoader => Some(Scala2CompilationUnit(cls.fullName.toString)) + case _ if cls.flags.is(Flags.JavaDefined) => Some(JavaCompilationUnit(cls.fullName.toString)) + case _ => alreadyLoaded() + } case _ => cannotUnpickle(s"its class file does not have a TASTY attribute") } diff --git a/compiler/src/dotty/tools/dotc/fromtasty/Scala2CompilationUnit.scala b/compiler/src/dotty/tools/dotc/fromtasty/Scala2CompilationUnit.scala new file mode 100644 index 000000000000..51ac0e52a3ed --- /dev/null +++ b/compiler/src/dotty/tools/dotc/fromtasty/Scala2CompilationUnit.scala @@ -0,0 +1,13 @@ +package dotty.tools.dotc.fromtasty + +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.util.NoSource + +/** A marker CompilationUnit to return up the call stack from ReadTasty. This will tell us that we've + * encountered, and attempted to inspect, a Scala2 class file (which has no .tasty file). + * In this case we still want to return the fact we found it so that higher-up we can take appropriate + * action if desired. + */ +class Scala2CompilationUnit(val className: String) extends CompilationUnit(NoSource) { + override def toString: String = s"Scala2 class file $className" +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala index 201d18e558e9..ab45d594f236 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala @@ -65,6 +65,14 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend def Context_requiredClass(self: Context)(path: String): Symbol = self.requiredClass(path) def Context_requiredModule(self: Context)(path: String): Symbol = self.requiredModule(path) def Context_requiredMethod(self: Context)(path: String): Symbol = self.requiredMethod(path) + def Context_isJavaCompilationUnit(self: Context): Boolean = self.compilationUnit.isInstanceOf[fromtasty.JavaCompilationUnit] + def Context_isScala2CompilationUnit(self: Context): Boolean = self.compilationUnit.isInstanceOf[fromtasty.Scala2CompilationUnit] + def Context_compilationUnitClassname(self: Context): String = + self.compilationUnit match { + case cu: fromtasty.JavaCompilationUnit => cu.className + case cu: fromtasty.Scala2CompilationUnit => cu.className + case cu => "" + } /////////////// diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index dfdf138bdb0c..eda41fba1959 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -473,6 +473,14 @@ class Reflection(private[scala] val internal: CompilerInterface) { self => /** Get method symbol if method is either defined in current compilation run or present on classpath. Throws if the method has an overload. */ def requiredMethod(path: String): Symbol = internal.Context_requiredMethod(self)(path) + /** Returns true if we've tried to reflect on a Java class. */ + def isJavaCompilationUnit(): Boolean = internal Context_isJavaCompilationUnit(self) + + /** Returns true if we've tried to reflect on a Scala2 (non-Tasty) class. */ + def isScala2CompilationUnit(): Boolean = internal Context_isScala2CompilationUnit(self) + + /** Class name of the current CompilationUnit */ + def compilationUnitClassname(): String = internal.Context_compilationUnitClassname(self) } diff --git a/library/src/scala/tasty/reflect/CompilerInterface.scala b/library/src/scala/tasty/reflect/CompilerInterface.scala index b2f08fc65e30..4d8bde281daf 100644 --- a/library/src/scala/tasty/reflect/CompilerInterface.scala +++ b/library/src/scala/tasty/reflect/CompilerInterface.scala @@ -162,6 +162,15 @@ trait CompilerInterface { /** Get method symbol if method is either defined in current compilation run or present on classpath. Throws if the method has an overload. */ def Context_requiredMethod(self: Context)(path: String): Symbol + /** Returns true if we've tried to reflect on a Java class. */ + def Context_isJavaCompilationUnit(self: Context): Boolean + + /** Returns true if we've tried to reflect on a Scala2 (non-Tasty) class. */ + def Context_isScala2CompilationUnit(self: Context): Boolean + + /** Class name of the current CompilationUnit */ + def Context_compilationUnitClassname(self: Context): String + /////////////// // REPORTING // diff --git a/tests/run-custom-args/tasty-inspector/i8215.scala b/tests/run-custom-args/tasty-inspector/i8215.scala new file mode 100644 index 000000000000..d244f720942e --- /dev/null +++ b/tests/run-custom-args/tasty-inspector/i8215.scala @@ -0,0 +1,42 @@ +import scala.tasty.Reflection +import scala.tasty.inspector._ + +case class I8215(id: String) + +object Test { + def main(args: Array[String]): Unit = { + + // Tasty Scala Class + val inspect1 = new TestInspector_NonTasty() + inspect1.inspect("", List("I8215")) + assert(inspect1.isJava == false) + assert(inspect1.isScala2 == false) + assert(inspect1.className == "") + + // Java Class + val inspect2 = new TestInspector_NonTasty() + inspect2.inspect("", List("java.util.UUID")) + assert(inspect2.isJava == true) + assert(inspect2.isScala2 == false) + assert(inspect2.className == "java.util.UUID") + + // Legacy non-Tasty Scala class + val inspect3 = new TestInspector_NonTasty() + inspect3.inspect("", List("scala.collection.immutable.RedBlackTree")) + assert(inspect3.isJava == false) + assert(inspect3.isScala2 == true) + assert(inspect3.className == "scala.collection.immutable.RedBlackTree") + } +} + +class TestInspector_NonTasty() extends TastyInspector: + + var isJava: Boolean = false + var isScala2: Boolean = false + var className: String = "" + + protected def processCompilationUnit(reflect: Reflection)(root: reflect.Tree): Unit = + import reflect.{_, given _} + isJava = reflect.rootContext.isJavaCompilationUnit() + isScala2 = reflect.rootContext.isScala2CompilationUnit() + className = reflect.rootContext.compilationUnitClassname()