Skip to content

classSymbol fullname not consistent with class name for inner classes #10933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
gaeljw opened this issue Dec 28, 2020 · 6 comments · Fixed by #11519
Closed

classSymbol fullname not consistent with class name for inner classes #10933

gaeljw opened this issue Dec 28, 2020 · 6 comments · Fixed by #11519
Assignees

Comments

@gaeljw
Copy link

gaeljw commented Dec 28, 2020

Minimized code

Consider the following macro:

import scala.quoted._

object Macros {

  inline def parameterTypes[T1](): String = {
    ${ getTypes[T1]() }
  }

  private def getTypes[T1]()(using Type[T1], Quotes): Expr[String] = {
    import quotes.reflect._
    Expr(TypeRepr.of[T1].classSymbol.get.fullName)
  }

}

And the following usage (as a test but doesn't matter) :

case class TopLevelClass(c: String)

object TestObject {

  case class ObjectInnerClass(b: String)

}

class Test1 {

  case class InnerClass(a: String)

  @Test def t1(): Unit = {
    val foundTypes = Macros.parameterTypes[InnerClass]()
    val foundTypesObj = Macros.parameterTypes[TestObject.ObjectInnerClass]()
    val foundTypesTL = Macros.parameterTypes[TopLevelClass]()

    val expectedTypes = classOf[InnerClass].getName()
    val expectedTypesObj = classOf[TestObject.ObjectInnerClass].getName()
    val expectedTypesTL = classOf[TopLevelClass].getName()

    println(foundTypes)
    println(expectedTypes)

    println(foundTypesObj)
    println(expectedTypesObj)

    println(foundTypesTL)
    println(expectedTypesTL)
  }

}

Output

Test1.InnerClass
Test1$InnerClass
TestObject$.ObjectInnerClass
TestObject$ObjectInnerClass
TopLevelClass
TopLevelClass

// Or with a package
my.project.Test1.InnerClass
my.project.Test1$InnerClass
my.project.TestObject$.ObjectInnerClass
my.project.TestObject$ObjectInnerClass
my.project.TopLevelClass
my.project.TopLevelClass

For inner classes, the name given by the macro is different of the actual class name.

Expectation

I expected the TypeRepr.of[T1].classSymbol.get.fullName to give the same name as would classOf[T1].getName.

To give some background, I'm later using the class name with Class.forName(name) and it fails for Class.forName("TestObject$.ObjectInnerClass") for instance.

If that's an expected behavior, what would be the way from TypeRepr.of[T1] to get the class name that can later be used to retrieve the actual class?

@abgruszecki
Copy link
Contributor

The name of a symbol and the name of a type are two different things, we probably can't conflate them in the Reflect API.

In most cases, if a symbol defines a class, then going through symbol -> tree -> type -> name should give a name that in most cases resolves back to the class. IIRC there are still edge-cases where it won't, such as names that contain dots.

/cc @nicolasstucki

@gaeljw
Copy link
Author

gaeljw commented Dec 28, 2020

@abgruszecki Could you please clarify the tree -> type -> name relationship?

I've tried TypeRepr.of[T1].classSymbol.get.tree.asInstanceOf[TypeDef].name but this only gives the class name without any package or even the outer class/object it belongs to.

@abgruszecki
Copy link
Contributor

Ah, I just took a look at the API and it doesn't look like what I had in mind is possible in Reflect, it was a path we could take inside the compiler. I'll need to experiment a bit.

@gaeljw
Copy link
Author

gaeljw commented Dec 29, 2020

Thanks a lot for taking some time to look at it.

To give some more background, my ultimate goal is somehow to replace the usage of Scala 2's TypeTags and get a Class[_] and its type parameters.
For instance parameterTypes[Seq[Int]] would give me something like:

TypeParametersInfo(
  clazz = classOf[Seq],
  typeArgs = Seq(
    TypeParametersInfo(
      clazz = classOf[Int],
      typeArgs = Nil
    )
  )
)

If I've got the class full names using macros and the implicit Types, I can manage the whole thing.

But maybe you can think of a totally different way to achieve this goal 😄

@gaeljw
Copy link
Author

gaeljw commented Feb 7, 2021

Relates to #11161

@gaeljw
Copy link
Author

gaeljw commented Feb 14, 2021

For anyone interested, I've solved my initial use case using a macro that gives me a ClassTag[_] rather than a Class[_].
You can see the code on the following project: https://github.com/gaeljw/typetrees/blob/main/src/main/scala/io/github/gaeljw/typetrees/TypeTreeTagMacros.scala#L8

@nicolasstucki nicolasstucki linked a pull request Feb 24, 2021 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants