diff --git a/metals/src/main/scala/scala/meta/internal/metals/mbt/VirtualTextDocument.scala b/metals/src/main/scala/scala/meta/internal/metals/mbt/VirtualTextDocument.scala index 54f790cf192b..3458bf65be88 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/mbt/VirtualTextDocument.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/mbt/VirtualTextDocument.scala @@ -6,6 +6,7 @@ import javax.tools.JavaFileObject.Kind import javax.tools.SimpleJavaFileObject import scala.meta.internal.metals.MetalsEnrichments._ +import scala.meta.internal.mtags.Symbol import scala.meta.internal.{semanticdb => s} import scala.meta.pc import scala.meta.pc.SemanticdbCompilationUnit @@ -32,7 +33,7 @@ final case class VirtualTextDocument( case None => pkg.replace('/', '.') case Some(sym) => - sym.stripSuffix("#").stripSuffix(".").replace('/', '.') + Symbol(sym).toplevelClassName } } override def getName(): String = uri.toString diff --git a/mtags/src/main/scala/scala/meta/internal/mtags/ClasspathDefinitionIndex.scala b/mtags/src/main/scala/scala/meta/internal/mtags/ClasspathDefinitionIndex.scala index 53bfb1f20960..2b0c2f23cb9c 100644 --- a/mtags/src/main/scala/scala/meta/internal/mtags/ClasspathDefinitionIndex.scala +++ b/mtags/src/main/scala/scala/meta/internal/mtags/ClasspathDefinitionIndex.scala @@ -117,8 +117,7 @@ class ClasspathDefinitionIndex(mtags: () => Mtags, dialect: Dialect) { symbol: Symbol ): Option[String] = { val jar = new JarFile(module.jar.toFile) - val classfileName = - s"${symbol.toplevel.value.stripSuffix("#").stripSuffix(".")}.class" + val classfileName = s"${symbol.toplevelBinaryName}.class" try { val entry = jar.getEntry(classfileName) if (entry != null) { diff --git a/mtags/src/main/scala/scala/meta/internal/mtags/Symbol.scala b/mtags/src/main/scala/scala/meta/internal/mtags/Symbol.scala index e83a65767196..748d49856eff 100644 --- a/mtags/src/main/scala/scala/meta/internal/mtags/Symbol.scala +++ b/mtags/src/main/scala/scala/meta/internal/mtags/Symbol.scala @@ -57,6 +57,22 @@ final class Symbol private (val value: String) { !value.isPackage && value.owner.isPackage } + + /** + * The JVMS 4.2.1 binary name of the toplevel class this symbol belongs to, + * for example `scala/Option#get().` becomes `scala/Option`. Toplevel, because + * a nested class is `Outer$Inner` in a binary name but `Outer#Inner#` here. + */ + def toplevelBinaryName: String = + toplevel.value.stripSuffix("#").stripSuffix(".") + + /** + * [[toplevelBinaryName]] with `.` between package parts, the form + * `Class.forName` expects: `scala/Option#get().` becomes `scala.Option`. + */ + def toplevelClassName: String = + toplevelBinaryName.replace('/', '.') + def asNonEmpty: Option[Symbol] = if (isNone) None else Some(this) @@ -86,6 +102,15 @@ object Symbol { else new Symbol(sym) } + /** + * The symbol of a toplevel class named the way `Class.forName` names it, the + * inverse of [[Symbol.toplevelClassName]]: `scala.Option` becomes + * `scala/Option#`. Toplevel, since a nested class is `Outer.Inner` there but + * `Outer#Inner#` here. + */ + def fromToplevelClassName(className: String): Symbol = + Symbol(className.replace('.', '/') + "#") + def validated(sym: String): Either[String, Symbol] = { // NOTE(olafur): this validation method is hacky, we should write a proper // parser that reports positioned error messages with actionable feedback diff --git a/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala b/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala index da0ca918156a..cbe16f79da99 100644 --- a/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala +++ b/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala @@ -373,7 +373,7 @@ class SymbolIndexBucket( // Output: scala/collection/immutable/List.scala // scala/collection/immutable/List.java private def trivialPaths(toplevel: Symbol): List[String] = { - val noExtension = toplevel.value.stripSuffix(".").stripSuffix("#") + val noExtension = toplevel.toplevelBinaryName List( noExtension + ".scala", noExtension + ".java" @@ -382,8 +382,8 @@ class SymbolIndexBucket( private def modulePaths(toplevel: Symbol): List[String] = { if (Properties.isJavaAtLeast("9")) { - val noExtension = toplevel.value.stripSuffix(".").stripSuffix("#") - val javaSymbol = noExtension.replace("/", ".") + val noExtension = toplevel.toplevelBinaryName + val javaSymbol = toplevel.toplevelClassName for { cls <- sourceJars.loadClassSafe(javaSymbol).toList // note(@tgodzik) Modules are only available in Java 9+, so we need to invoke this reflectively