diff --git a/metals-bench/src/main/scala/bench/MetalsBench.scala b/metals-bench/src/main/scala/bench/MetalsBench.scala index 5715745a5de..4c5692412be 100644 --- a/metals-bench/src/main/scala/bench/MetalsBench.scala +++ b/metals-bench/src/main/scala/bench/MetalsBench.scala @@ -11,6 +11,7 @@ import scala.meta.internal.metals.JdkSources import scala.meta.internal.metals.ReportContext import scala.meta.internal.metals.logging.MetalsLogger import scala.meta.internal.mtags.JavaMtags +import scala.meta.internal.mtags.JavaToplevelMtags import scala.meta.internal.mtags.Mtags import scala.meta.internal.mtags.OnDemandSymbolIndex import scala.meta.internal.mtags.ScalaMtags @@ -179,6 +180,14 @@ class MetalsBench { } } + @Benchmark + @BenchmarkMode(Array(Mode.SingleShotTime)) + def toplevelJavaMtags(): Unit = { + javaDependencySources.inputs.foreach { input => + new JavaToplevelMtags(input, includeInnerClasses = true).index() + } + } + @Benchmark @BenchmarkMode(Array(Mode.SingleShotTime)) def indexSources(): Unit = { diff --git a/metals/src/main/resources/db/migration/V6__Delete_indices.sql b/metals/src/main/resources/db/migration/V6__Delete_indices.sql new file mode 100644 index 00000000000..e8eafcf2d19 --- /dev/null +++ b/metals/src/main/resources/db/migration/V6__Delete_indices.sql @@ -0,0 +1,2 @@ +-- indexing type hierarchy has changed, so we want to reindex +delete from indexed_jar; diff --git a/mtags/src/main/scala/scala/meta/internal/mtags/JavaToplevelMtags.scala b/mtags/src/main/scala/scala/meta/internal/mtags/JavaToplevelMtags.scala index 4c57145b317..d9a4aec4e9a 100644 --- a/mtags/src/main/scala/scala/meta/internal/mtags/JavaToplevelMtags.scala +++ b/mtags/src/main/scala/scala/meta/internal/mtags/JavaToplevelMtags.scala @@ -10,7 +10,10 @@ import scala.meta.internal.semanticdb.SymbolInformation import scala.meta.internal.tokenizers.Chars._ import scala.meta.internal.tokenizers.Reporter -class JavaToplevelMtags(val input: Input.VirtualFile) extends MtagsIndexer { +class JavaToplevelMtags( + val input: Input.VirtualFile, + includeInnerClasses: Boolean +) extends MtagsIndexer { import JavaToplevelMtags._ @@ -18,12 +21,20 @@ class JavaToplevelMtags(val input: Input.VirtualFile) extends MtagsIndexer { val reader: CharArrayReader = new CharArrayReader(input, dialects.Scala213, reporter) + override def overrides(): List[(String, List[OverriddenSymbol])] = + overridden.result + + private val overridden = List.newBuilder[(String, List[OverriddenSymbol])] + + private def addOverridden(symbols: List[OverriddenSymbol]) = + overridden += ((currentOwner, symbols)) + override def language: Language = Language.JAVA override def indexRoot(): Unit = { if (!input.path.endsWith("module-info.java")) { reader.nextRawChar() - loop + loop(None) } } @@ -35,14 +46,15 @@ class JavaToplevelMtags(val input: Input.VirtualFile) extends MtagsIndexer { } } - private def loop: Unit = { + @tailrec + private def loop(region: Option[Region]): Unit = { val token = fetchToken token match { case Token.EOF => case Token.Package => val paths = readPaths paths.foreach { path => pkg(path.value, path.pos) } - loop + loop(region) case Token.Class | Token.Interface | _: Token.Enum | _: Token.Record => fetchToken match { case Token.Word(v, pos) => @@ -50,14 +62,74 @@ class JavaToplevelMtags(val input: Input.VirtualFile) extends MtagsIndexer { case Token.Interface => SymbolInformation.Kind.INTERFACE case _ => SymbolInformation.Kind.CLASS } - withOwner(currentOwner)(tpe(v, pos, kind, 0)) - skipBody - loop + val previousOwner = currentOwner + tpe(v, pos, kind, 0) + if (includeInnerClasses) { + collectTypeHierarchyInformation + loop(Some(Region(region, currentOwner, lBraceCount = 1))) + } else { + skipBody + currentOwner = previousOwner + loop(region) + } + case Token.LBrace => + loop(region.map(_.lBrace())) + case Token.RBrace => + val newRegion = region.flatMap(_.rBrace()) + newRegion.foreach(reg => currentOwner = reg.owner) + loop(newRegion) case _ => - loop + loop(region) } + case Token.LBrace => + loop(region.map(_.lBrace())) + case Token.RBrace => + val newRegion = region.flatMap(_.rBrace()) + newRegion.foreach(reg => currentOwner = reg.owner) + loop(newRegion) + case _ => + loop(region) + } + } + + private def collectTypeHierarchyInformation: Unit = { + val implementsOrExtends = List.newBuilder[String] + @tailrec + def skipUntilOptImplementsOrExtends: Token = { + fetchToken match { + case t @ (Token.Implements | Token.Extends) => t + case Token.EOF => Token.EOF + case Token.LBrace => Token.LBrace + case _ => skipUntilOptImplementsOrExtends + } + } + + @tailrec + def collectHierarchy: Unit = { + fetchToken match { + case Token.Word(v, _) => + // emit here + implementsOrExtends += v + collectHierarchy + case Token.LBrace => + case Token.LParen => + skipBalanced(Token.LParen, Token.RParen) + collectHierarchy + case Token.LessThan => + skipBalanced(Token.LessThan, Token.GreaterThan) + collectHierarchy + case Token.EOF => + case _ => collectHierarchy + } + } + + skipUntilOptImplementsOrExtends match { + case Token.Implements | Token.Extends => + collectHierarchy + addOverridden( + implementsOrExtends.result.distinct.map(UnresolvedOverriddenSymbol(_)) + ) case _ => - loop } } @@ -103,6 +175,8 @@ class JavaToplevelMtags(val input: Input.VirtualFile) extends MtagsIndexer { case "interface" => Token.Interface case "record" => Token.Record(pos) case "enum" => Token.Enum(pos) + case "extends" => Token.Extends + case "implements" => Token.Implements case ident => Token.Word(ident, pos) } @@ -113,8 +187,8 @@ class JavaToplevelMtags(val input: Input.VirtualFile) extends MtagsIndexer { def parseToken: (Token, Boolean) = { val first = reader.ch first match { - case ',' | '<' | '>' | '&' | '|' | '!' | '=' | '+' | '-' | '*' | '@' | - ':' | '?' | '%' | '^' | '~' => + case ',' | '&' | '|' | '!' | '=' | '+' | '-' | '*' | '@' | ':' | '?' | + '%' | '^' | '~' => (Token.SpecialSym, false) case SU => (Token.EOF, false) case '.' => (Token.Dot, false) @@ -125,6 +199,8 @@ class JavaToplevelMtags(val input: Input.VirtualFile) extends MtagsIndexer { case ')' => (Token.RParen, false) case '[' => (Token.LBracket, false) case ']' => (Token.RBracket, false) + case '<' => (Token.LessThan, false) + case '>' => (Token.GreaterThan, false) case '"' => (quotedLiteral('"'), false) case '\'' => (quotedLiteral('\''), false) case '/' => @@ -190,22 +266,26 @@ class JavaToplevelMtags(val input: Input.VirtualFile) extends MtagsIndexer { skipToFirstBrace } - @tailrec - def skipToRbrace(open: Int): Unit = { - fetchToken match { - case Token.RBrace if open == 1 => () - case Token.RBrace => - skipToRbrace(open - 1) - case Token.LBrace => - skipToRbrace(open + 1) - case Token.EOF => () - case _ => - skipToRbrace(open) - } - } - skipToFirstBrace - skipToRbrace(1) + skipBalanced(Token.LBrace, Token.RBrace) + } + + @tailrec + private def skipBalanced( + openingToken: Token, + closingToken: Token, + open: Int = 1 + ): Unit = { + fetchToken match { + case t if t == closingToken && open == 1 => () + case t if t == closingToken => + skipBalanced(openingToken, closingToken, open - 1) + case t if t == openingToken => + skipBalanced(openingToken, closingToken, open + 1) + case Token.EOF => () + case _ => + skipBalanced(openingToken, closingToken, open) + } } private def skipLine: Unit = @@ -260,12 +340,16 @@ object JavaToplevelMtags { case class Record(pos: Position) extends WithPos { val value: String = "record" } + case object Implements extends Token + case object Extends extends Token case object RBrace extends Token case object LBrace extends Token case object RParen extends Token case object LParen extends Token case object RBracket extends Token case object LBracket extends Token + case object LessThan extends Token + case object GreaterThan extends Token case object Semicolon extends Token // any allowed symbol like `=` , `-` and others case object SpecialSym extends Token @@ -277,4 +361,15 @@ object JavaToplevelMtags { } } + + case class Region( + previousRegion: Option[Region], + owner: String, + lBraceCount: Int + ) { + def lBrace(): Region = Region(previousRegion, owner, lBraceCount + 1) + def rBrace(): Option[Region] = + if (lBraceCount == 1) previousRegion + else Some(Region(previousRegion, owner, lBraceCount - 1)) + } } diff --git a/mtags/src/main/scala/scala/meta/internal/mtags/Mtags.scala b/mtags/src/main/scala/scala/meta/internal/mtags/Mtags.scala index e52b9fcfa11..186269fa1ac 100644 --- a/mtags/src/main/scala/scala/meta/internal/mtags/Mtags.scala +++ b/mtags/src/main/scala/scala/meta/internal/mtags/Mtags.scala @@ -31,7 +31,7 @@ final class Mtags(implicit rc: ReportContext) { if (language.isJava || language.isScala) { val mtags = if (language.isJava) - new JavaToplevelMtags(input) + new JavaToplevelMtags(input, includeInnerClasses = false) else new ScalaToplevelMtags( input, @@ -59,7 +59,7 @@ final class Mtags(implicit rc: ReportContext) { if (language.isJava || language.isScala) { val mtags = if (language.isJava) - new JavaToplevelMtags(input) + new JavaToplevelMtags(input, includeInnerClasses = true) else new ScalaToplevelMtags( input, diff --git a/mtags/src/main/scala/scala/meta/internal/mtags/OnDemandSymbolIndex.scala b/mtags/src/main/scala/scala/meta/internal/mtags/OnDemandSymbolIndex.scala index a7b47aa3ba3..c6699b08814 100644 --- a/mtags/src/main/scala/scala/meta/internal/mtags/OnDemandSymbolIndex.scala +++ b/mtags/src/main/scala/scala/meta/internal/mtags/OnDemandSymbolIndex.scala @@ -114,7 +114,8 @@ final class OnDemandSymbolIndex( source, None, { indexedSources += 1 - getOrCreateBucket(dialect).addSourceFile(source, sourceDirectory) + getOrCreateBucket(dialect) + .addSourceFile(source, sourceDirectory, isJava = false) } ) 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 655517f0b74..fac56574ee5 100644 --- a/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala +++ b/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala @@ -52,7 +52,7 @@ class SymbolIndexBucket( if (sourceJars.addEntry(dir.toNIO)) { dir.listRecursive.toList.flatMap { case source if source.isScala => - addSourceFile(source, Some(dir)) + addSourceFile(source, Some(dir), isJava = false) case _ => None } @@ -67,13 +67,9 @@ class SymbolIndexBucket( try { root.listRecursive.toList.flatMap { case source if source.isScala => - addSourceFile(source, None) + addSourceFile(source, None, isJava = false) case source if source.isJava => - addJavaSourceFile(source) match { - case Nil => None - case topLevels => - Some(IndexingResult(source, topLevels, overrides = Nil)) - } + addSourceFile(source, None, isJava = true) case _ => None } @@ -100,39 +96,13 @@ class SymbolIndexBucket( } } - /* Sometimes source jars have additional nested directories, - * in that case java toplevel is not "trivial". - * See: https://github.com/scalameta/metals/issues/3815 - */ - def addJavaSourceFile(source: AbsolutePath): List[String] = { - new JavaToplevelMtags(source.toInput).readPackage match { - case Nil => Nil - case packageParts => - val className = source.filename.stripSuffix(".java") - val symbol = packageParts.mkString("", "/", s"/$className#") - if ( - isTrivialToplevelSymbol( - source.toURI.toString, - symbol, - extension = "java" - ) - ) Nil - else { - toplevels.updateWith(symbol) { - case Some(acc) => Some(acc + source) - case None => Some(Set(source)) - } - List(symbol) - } - } - } - def addSourceFile( source: AbsolutePath, - sourceDirectory: Option[AbsolutePath] + sourceDirectory: Option[AbsolutePath], + isJava: Boolean ): Option[IndexingResult] = { val IndexingResult(path, topLevels, overrides) = - indexSource(source, dialect, sourceDirectory) + indexSource(source, dialect, sourceDirectory, isJava) topLevels.foreach { symbol => toplevels.updateWith(symbol) { case Some(acc) => Some(acc + source) @@ -145,7 +115,8 @@ class SymbolIndexBucket( private def indexSource( source: AbsolutePath, dialect: Dialect, - sourceDirectory: Option[AbsolutePath] + sourceDirectory: Option[AbsolutePath], + isJava: Boolean ): IndexingResult = { val uri = source.toIdeallyRelativeURI(sourceDirectory) val (doc, overrides) = mtags.indexWithOverrides(source, dialect) @@ -155,8 +126,15 @@ class SymbolIndexBucket( .map(_.symbol) val topLevels = if (source.isAmmoniteScript) sourceTopLevels.toList - else - sourceTopLevels.filter(sym => !isTrivialToplevelSymbol(uri, sym)).toList + else if (isJava) { + sourceTopLevels.toList.headOption + .filter(sym => !isTrivialToplevelSymbol(uri, sym, "java")) + .toList + } else { + sourceTopLevels + .filter(sym => !isTrivialToplevelSymbol(uri, sym, "scala")) + .toList + } IndexingResult(source, topLevels, overrides) } diff --git a/tests/unit/src/main/scala/tests/BaseImplementationSuite.scala b/tests/unit/src/main/scala/tests/BaseImplementationSuite.scala index 3626bbcb383..e0fa2cbbc71 100644 --- a/tests/unit/src/main/scala/tests/BaseImplementationSuite.scala +++ b/tests/unit/src/main/scala/tests/BaseImplementationSuite.scala @@ -26,6 +26,7 @@ abstract class BaseImplementationSuite(name: String) fileContents: String, expectedSymbols: String, scalaVersion: String = BuildInfo.scalaVersion, + topLines: Option[Int] = None, ): Unit = test(name) { val fileName = "a/src/main/scala/a/Main.scala" @@ -53,7 +54,8 @@ abstract class BaseImplementationSuite(name: String) ) ) symbols = definitions.map(_.symbol).sorted - _ = assertNoDiff(symbols.mkString("\n"), expectedSymbols) + foundSymbols = topLines.map(num => symbols.take(num)).getOrElse(symbols) + _ = assertNoDiff(foundSymbols.mkString("\n"), expectedSymbols) _ <- server.shutdown() } yield () } diff --git a/tests/unit/src/test/scala/tests/BaseToplevelSuite.scala b/tests/unit/src/test/scala/tests/BaseToplevelSuite.scala new file mode 100644 index 00000000000..1b8a7731ed7 --- /dev/null +++ b/tests/unit/src/test/scala/tests/BaseToplevelSuite.scala @@ -0,0 +1,85 @@ +package tests + +import java.nio.file.Files + +import scala.meta.Dialect +import scala.meta.dialects +import scala.meta.internal.metals.MetalsEnrichments._ +import scala.meta.internal.mtags.Mtags +import scala.meta.internal.mtags.ResolvedOverriddenSymbol +import scala.meta.internal.mtags.UnresolvedOverriddenSymbol +import scala.meta.io.AbsolutePath + +import munit.TestOptions + +abstract class BaseToplevelSuite extends BaseSuite { + + def filename: String = "Test.scala" + def allowedModes: Set[Mode] = Set(All, Toplevel, ToplevelWithInner) + + def check( + options: TestOptions, + code: String, + expected: List[String], + mode: Mode = Toplevel, + dialect: Dialect = dialects.Scala3, + )(implicit location: munit.Location): Unit = { + test(options) { + if (!allowedModes(mode)) { + throw new Exception(s"Mode $mode is not allowed.") + } + val dir = AbsolutePath(Files.createTempDirectory("mtags")) + val input = dir.resolve(filename) + input.writeText(code) + val obtained = + mode match { + case All | ToplevelWithInner => + val includeMembers = mode == All + val (doc, overrides) = + Mtags.indexWithOverrides(input, dialect, includeMembers) + val overriddenMap = overrides.toMap + doc.symbols.map { symbolInfo => + val suffix = if (symbolInfo.isExtension) " EXT" else "" + val symbol = symbolInfo.symbol + overriddenMap.get(symbol) match { + case None => s"$symbol$suffix" + case Some(symbols) => + val overridden = + symbols + .map { + case ResolvedOverriddenSymbol(symbol) => symbol + case UnresolvedOverriddenSymbol(name) => name + } + .mkString(", ") + s"$symbol$suffix -> $overridden" + } + } + case Toplevel => Mtags.topLevelSymbols(input, dialect) + } + input.delete() + dir.delete() + assertNoDiff( + obtained.sorted.mkString("\n"), + expected.sorted.mkString("\n"), + ) + } + } + + def assertToplevelsNoDiff( + obtained: List[String], + expected: List[String], + ): Unit = { + assertNoDiff(obtained.sorted.mkString("\n"), expected.sorted.mkString("\n")) + } + + sealed trait Mode + // includeInnerClasses = false + // includeMembers = false + case object Toplevel extends Mode + // includeInnerClasses = true + // includeMembers = false + case object ToplevelWithInner extends Mode + // includeInnerClasses = true + // includeMembers = true + case object All extends Mode +} diff --git a/tests/unit/src/test/scala/tests/ImplementationLspSuite.scala b/tests/unit/src/test/scala/tests/ImplementationLspSuite.scala index b0e4cc88f50..870d8004151 100644 --- a/tests/unit/src/test/scala/tests/ImplementationLspSuite.scala +++ b/tests/unit/src/test/scala/tests/ImplementationLspSuite.scala @@ -254,9 +254,8 @@ class ImplementationLspSuite extends BaseImplementationSuite("implementation") { |""".stripMargin, ) - // we currently don't collect information about overridden symbols in JavaMtags check( - "java-classes".ignore, + "java-classes", """|/a/src/main/scala/a/Main.scala |package a |class <> extends Exce@@ption @@ -641,23 +640,516 @@ class ImplementationLspSuite extends BaseImplementationSuite("implementation") { |""".stripMargin, ) - checkSymbols( - "exception", - """package a - |class MyException extends Excep@@tion - |""".stripMargin, - """|a/MyException# - |scala/ScalaReflectionException# - |scala/reflect/internal/FatalError# - |scala/reflect/internal/MissingRequirementError# - |scala/reflect/internal/Positions#ValidateException# - |scala/reflect/macros/Enclosures#EnclosureException# - |scala/reflect/macros/ParseException# - |scala/reflect/macros/ReificationException# - |scala/reflect/macros/TypecheckException# - |scala/reflect/macros/UnexpectedReificationException# - |""".stripMargin, - ) + if (!isJava8) { + checkSymbols( + "exception", + """package a + |class MyException extends Excep@@tion + |""".stripMargin, + """|a/MyException# + |com/sun/beans/finder/SignatureException# + |com/sun/imageio/plugins/jpeg/JFIFMarkerSegment#IllegalThumbException# + |com/sun/jdi/AbsentInformationException# + |com/sun/jdi/ClassNotLoadedException# + |com/sun/jdi/ClassNotPreparedException# + |com/sun/jdi/IncompatibleThreadStateException# + |com/sun/jdi/InconsistentDebugInfoException# + |com/sun/jdi/InternalException# + |com/sun/jdi/InvalidCodeIndexException# + |com/sun/jdi/InvalidLineNumberException# + |com/sun/jdi/InvalidModuleException# + |com/sun/jdi/InvalidStackFrameException# + |com/sun/jdi/InvalidTypeException# + |com/sun/jdi/InvocationException# + |com/sun/jdi/NativeMethodException# + |com/sun/jdi/ObjectCollectedException# + |com/sun/jdi/VMCannotBeModifiedException# + |com/sun/jdi/VMDisconnectedException# + |com/sun/jdi/VMMismatchException# + |com/sun/jdi/VMOutOfMemoryException# + |com/sun/jdi/connect/IllegalConnectorArgumentsException# + |com/sun/jdi/connect/TransportTimeoutException# + |com/sun/jdi/connect/VMStartException# + |com/sun/jdi/connect/spi/ClosedConnectionException# + |com/sun/jdi/request/DuplicateRequestException# + |com/sun/jdi/request/InvalidRequestStateException# + |com/sun/jndi/ldap/LdapReferralException# + |com/sun/media/sound/InvalidDataException# + |com/sun/media/sound/InvalidFormatException# + |com/sun/media/sound/RIFFInvalidDataException# + |com/sun/media/sound/RIFFInvalidFormatException# + |com/sun/nio/sctp/IllegalReceiveException# + |com/sun/nio/sctp/IllegalUnbindException# + |com/sun/nio/sctp/InvalidStreamException# + |com/sun/org/apache/bcel/internal/classfile/ClassFormatException# + |com/sun/org/apache/bcel/internal/generic/ClassGenException# + |com/sun/org/apache/bcel/internal/generic/TargetLostException# + |com/sun/org/apache/xalan/internal/xsltc/TransletException# + |com/sun/org/apache/xalan/internal/xsltc/compiler/CompilerException# + |com/sun/org/apache/xalan/internal/xsltc/compiler/IllegalCharException# + |com/sun/org/apache/xalan/internal/xsltc/compiler/util/TypeCheckError# + |com/sun/org/apache/xerces/internal/dom/AbortException# + |com/sun/org/apache/xerces/internal/dom/RangeExceptionImpl# + |com/sun/org/apache/xerces/internal/impl/dv/DVFactoryException# + |com/sun/org/apache/xerces/internal/impl/dv/DatatypeException# + |com/sun/org/apache/xerces/internal/impl/dv/InvalidDatatypeFacetException# + |com/sun/org/apache/xerces/internal/impl/dv/InvalidDatatypeValueException# + |com/sun/org/apache/xerces/internal/impl/dv/xs/SchemaDateTimeException# + |com/sun/org/apache/xerces/internal/impl/io/MalformedByteSequenceException# + |com/sun/org/apache/xerces/internal/impl/xpath/XPathException# + |com/sun/org/apache/xerces/internal/impl/xpath/regex/ParseException# + |com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaException# + |com/sun/org/apache/xerces/internal/jaxp/validation/WrappedSAXException# + |com/sun/org/apache/xerces/internal/xni/XNIException# + |com/sun/org/apache/xerces/internal/xni/parser/XMLConfigurationException# + |com/sun/org/apache/xerces/internal/xni/parser/XMLParseException# + |com/sun/org/apache/xerces/internal/xs/XSException# + |com/sun/org/apache/xml/internal/dtm/DTMDOMException# + |com/sun/org/apache/xml/internal/dtm/DTMException# + |com/sun/org/apache/xml/internal/dtm/ref/DTMNamedNodeMap#DTMException# + |com/sun/org/apache/xml/internal/dtm/ref/IncrementalSAXSource_Filter#StopException# + |com/sun/org/apache/xml/internal/security/c14n/CanonicalizationException# + |com/sun/org/apache/xml/internal/security/c14n/InvalidCanonicalizerException# + |com/sun/org/apache/xml/internal/security/exceptions/AlgorithmAlreadyRegisteredException# + |com/sun/org/apache/xml/internal/security/exceptions/Base64DecodingException# + |com/sun/org/apache/xml/internal/security/exceptions/XMLSecurityException# + |com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolverException# + |com/sun/org/apache/xml/internal/security/keys/storage/StorageResolverException# + |com/sun/org/apache/xml/internal/security/parser/XMLParserException# + |com/sun/org/apache/xml/internal/security/signature/InvalidDigestValueException# + |com/sun/org/apache/xml/internal/security/signature/InvalidSignatureValueException# + |com/sun/org/apache/xml/internal/security/signature/MissingResourceFailureException# + |com/sun/org/apache/xml/internal/security/signature/ReferenceNotInitializedException# + |com/sun/org/apache/xml/internal/security/signature/XMLSignatureException# + |com/sun/org/apache/xml/internal/security/transforms/InvalidTransformException# + |com/sun/org/apache/xml/internal/security/transforms/TransformationException# + |com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverException# + |com/sun/org/apache/xml/internal/serializer/utils/WrappedRuntimeException# + |com/sun/org/apache/xml/internal/utils/StopParseException# + |com/sun/org/apache/xml/internal/utils/WrappedRuntimeException# + |com/sun/org/apache/xml/internal/utils/WrongParserException# + |com/sun/org/apache/xpath/internal/FoundIndex# + |com/sun/org/apache/xpath/internal/XPathException# + |com/sun/org/apache/xpath/internal/XPathProcessorException# + |com/sun/org/apache/xpath/internal/functions/WrongNumberArgsException# + |com/sun/security/ntlm/NTLMException# + |com/sun/tools/attach/AgentInitializationException# + |com/sun/tools/attach/AgentLoadException# + |com/sun/tools/attach/AttachNotSupportedException# + |com/sun/tools/attach/AttachOperationFailedException# + |com/sun/tools/classfile/AttributeException# + |com/sun/tools/classfile/ConstantPoolException# + |com/sun/tools/classfile/DescriptorException# + |com/sun/tools/example/debug/expr/ParseException# + |com/sun/tools/example/debug/tty/AmbiguousMethodException# + |com/sun/tools/example/debug/tty/LineNotFoundException# + |com/sun/tools/example/debug/tty/MalformedMemberNameException# + |com/sun/tools/example/debug/tty/VMNotConnectedException# + |com/sun/tools/javac/launcher/Main#Fault# + |com/sun/tools/javac/util/ClientCodeException# + |com/sun/tools/javac/util/PropagatedException# + |com/sun/tools/javap/JavapTask#BadArgs# + |com/sun/tools/jdeprscan/CSVParseException# + |com/sun/tools/jdeps/MultiReleaseException# + |com/sun/tools/jdi/JDWPException# + |com/sun/tools/sjavac/ProblemException# + |com/sun/tools/sjavac/client/PortFileInaccessibleException# + |java/awt/AWTException# + |java/awt/FontFormatException# + |java/awt/HeadlessException# + |java/awt/IllegalComponentStateException# + |java/awt/color/CMMException# + |java/awt/color/ProfileDataException# + |java/awt/datatransfer/MimeTypeParseException# + |java/awt/datatransfer/UnsupportedFlavorException# + |java/awt/dnd/InvalidDnDOperationException# + |java/awt/geom/IllegalPathStateException# + |java/awt/geom/NoninvertibleTransformException# + |java/awt/image/ImagingOpException# + |java/awt/image/RasterFormatException# + |java/awt/print/PrinterAbortException# + |java/awt/print/PrinterException# + |java/awt/print/PrinterIOException# + |java/beans/IntrospectionException# + |java/beans/PropertyVetoException# + |java/io/CharConversionException# + |java/io/EOFException# + |java/io/FileNotFoundException# + |java/io/IOException# + |java/io/InterruptedIOException# + |java/io/InvalidClassException# + |java/io/InvalidObjectException# + |java/io/NotActiveException# + |java/io/NotSerializableException# + |java/io/ObjectStreamException# + |java/io/OptionalDataException# + |java/io/StreamCorruptedException# + |java/io/SyncFailedException# + |java/io/UTFDataFormatException# + |java/io/UncheckedIOException# + |java/io/UnsupportedEncodingException# + |java/io/WriteAbortedException# + |java/lang/ArithmeticException# + |java/lang/ArrayIndexOutOfBoundsException# + |java/lang/ArrayStoreException# + |java/lang/ClassCastException# + |java/lang/ClassNotFoundException# + |java/lang/CloneNotSupportedException# + |java/lang/EnumConstantNotPresentException# + |java/lang/IllegalAccessException# + |java/lang/IllegalArgumentException# + |java/lang/IllegalCallerException# + |java/lang/IllegalMonitorStateException# + |java/lang/IllegalStateException# + |java/lang/IllegalThreadStateException# + |java/lang/IndexOutOfBoundsException# + |java/lang/InstantiationException# + |java/lang/InterruptedException# + |java/lang/LayerInstantiationException# + |java/lang/NegativeArraySizeException# + |java/lang/NoSuchFieldException# + |java/lang/NoSuchMethodException# + |java/lang/NullPointerException# + |java/lang/NumberFormatException# + |java/lang/ReflectiveOperationException# + |java/lang/RuntimeException# + |java/lang/SecurityException# + |java/lang/StringIndexOutOfBoundsException# + |java/lang/TypeNotPresentException# + |java/lang/UnsupportedOperationException# + |java/lang/annotation/AnnotationTypeMismatchException# + |java/lang/annotation/IncompleteAnnotationException# + |java/lang/instrument/IllegalClassFormatException# + |java/lang/instrument/UnmodifiableClassException# + |java/lang/instrument/UnmodifiableModuleException# + |java/lang/invoke/LambdaConversionException# + |java/lang/invoke/StringConcatException# + |java/lang/invoke/WrongMethodTypeException# + |java/lang/module/FindException# + |java/lang/module/InvalidModuleDescriptorException# + |java/lang/module/ResolutionException# + |java/lang/reflect/InaccessibleObjectException# + |java/lang/reflect/InvocationTargetException# + |java/lang/reflect/MalformedParameterizedTypeException# + |java/lang/reflect/MalformedParametersException# + |java/lang/reflect/UndeclaredThrowableException# + |java/net/BindException# + |java/net/ConnectException# + |java/net/HttpRetryException# + |java/net/MalformedURLException# + |java/net/NoRouteToHostException# + |java/net/PortUnreachableException# + |java/net/ProtocolException# + |java/net/SocketException# + |java/net/SocketTimeoutException# + |java/net/URISyntaxException# + |java/net/UnknownHostException# + |java/net/UnknownServiceException# + |java/net/http/HttpConnectTimeoutException# + |java/net/http/HttpTimeoutException# + |java/net/http/WebSocketHandshakeException# + |java/nio/BufferOverflowException# + |java/nio/BufferUnderflowException# + |java/nio/InvalidMarkException# + |java/nio/ReadOnlyBufferException# + |java/nio/channels/AcceptPendingException# + |java/nio/channels/AlreadyBoundException# + |java/nio/channels/AlreadyConnectedException# + |java/nio/channels/AsynchronousCloseException# + |java/nio/channels/CancelledKeyException# + |java/nio/channels/ClosedByInterruptException# + |java/nio/channels/ClosedChannelException# + |java/nio/channels/ClosedSelectorException# + |java/nio/channels/ConnectionPendingException# + |java/nio/channels/FileLockInterruptionException# + |java/nio/channels/IllegalBlockingModeException# + |java/nio/channels/IllegalChannelGroupException# + |java/nio/channels/IllegalSelectorException# + |java/nio/channels/InterruptedByTimeoutException# + |java/nio/channels/NoConnectionPendingException# + |java/nio/channels/NonReadableChannelException# + |java/nio/channels/NonWritableChannelException# + |java/nio/channels/NotYetBoundException# + |java/nio/channels/NotYetConnectedException# + |java/nio/channels/OverlappingFileLockException# + |java/nio/channels/ReadPendingException# + |java/nio/channels/ShutdownChannelGroupException# + |java/nio/channels/UnresolvedAddressException# + |java/nio/channels/UnsupportedAddressTypeException# + |java/nio/channels/WritePendingException# + |java/nio/charset/CharacterCodingException# + |java/nio/charset/IllegalCharsetNameException# + |java/nio/charset/MalformedInputException# + |java/nio/charset/UnmappableCharacterException# + |java/nio/charset/UnsupportedCharsetException# + |java/nio/file/AccessDeniedException# + |java/nio/file/AtomicMoveNotSupportedException# + |java/nio/file/ClosedDirectoryStreamException# + |java/nio/file/ClosedFileSystemException# + |java/nio/file/ClosedWatchServiceException# + |java/nio/file/DirectoryIteratorException# + |java/nio/file/DirectoryNotEmptyException# + |java/nio/file/FileAlreadyExistsException# + |java/nio/file/FileSystemAlreadyExistsException# + |java/nio/file/FileSystemException# + |java/nio/file/FileSystemLoopException# + |java/nio/file/FileSystemNotFoundException# + |java/nio/file/InvalidPathException# + |java/nio/file/NoSuchFileException# + |java/nio/file/NotDirectoryException# + |java/nio/file/NotLinkException# + |java/nio/file/ProviderMismatchException# + |java/nio/file/ProviderNotFoundException# + |java/nio/file/ReadOnlyFileSystemException# + |java/nio/file/attribute/UserPrincipalNotFoundException# + |java/rmi/AccessException# + |java/rmi/AlreadyBoundException# + |java/rmi/ConnectException# + |java/rmi/ConnectIOException# + |java/rmi/MarshalException# + |java/rmi/NoSuchObjectException# + |java/rmi/NotBoundException# + |java/rmi/RMISecurityException# + |java/rmi/RemoteException# + |java/rmi/ServerError# + |java/rmi/ServerException# + |java/rmi/ServerRuntimeException# + |java/rmi/StubNotFoundException# + |java/rmi/UnexpectedException# + |java/rmi/UnknownHostException# + |java/rmi/UnmarshalException# + |java/rmi/server/ExportException# + |java/rmi/server/ServerCloneException# + |java/rmi/server/ServerNotActiveException# + |java/rmi/server/SkeletonMismatchException# + |java/rmi/server/SkeletonNotFoundException# + |java/rmi/server/SocketSecurityException# + |java/security/AccessControlException# + |java/security/DigestException# + |java/security/GeneralSecurityException# + |java/security/InvalidAlgorithmParameterException# + |java/security/InvalidKeyException# + |java/security/InvalidParameterException# + |java/security/KeyException# + |java/security/KeyManagementException# + |java/security/KeyStoreException# + |java/security/NoSuchAlgorithmException# + |java/security/NoSuchProviderException# + |java/security/PrivilegedActionException# + |java/security/ProviderException# + |java/security/SignatureException# + |java/security/UnrecoverableEntryException# + |java/security/UnrecoverableKeyException# + |java/security/cert/CRLException# + |java/security/cert/CertPathBuilderException# + |java/security/cert/CertPathValidatorException# + |java/security/cert/CertStoreException# + |java/security/cert/CertificateEncodingException# + |java/security/cert/CertificateException# + |java/security/cert/CertificateExpiredException# + |java/security/cert/CertificateNotYetValidException# + |java/security/cert/CertificateParsingException# + |java/security/cert/CertificateRevokedException# + |java/security/spec/InvalidKeySpecException# + |java/security/spec/InvalidParameterSpecException# + |java/sql/BatchUpdateException# + |java/sql/DataTruncation# + |java/sql/SQLClientInfoException# + |java/sql/SQLDataException# + |java/sql/SQLException# + |java/sql/SQLFeatureNotSupportedException# + |java/sql/SQLIntegrityConstraintViolationException# + |java/sql/SQLInvalidAuthorizationSpecException# + |java/sql/SQLNonTransientConnectionException# + |java/sql/SQLNonTransientException# + |java/sql/SQLRecoverableException# + |java/sql/SQLSyntaxErrorException# + |java/sql/SQLTimeoutException# + |java/sql/SQLTransactionRollbackException# + |java/sql/SQLTransientConnectionException# + |java/sql/SQLTransientException# + |java/sql/SQLWarning# + |java/text/ParseException# + |java/time/DateTimeException# + |java/time/format/DateTimeParseException# + |java/time/temporal/UnsupportedTemporalTypeException# + |java/time/zone/ZoneRulesException# + |java/util/ConcurrentModificationException# + |java/util/DuplicateFormatFlagsException# + |java/util/EmptyStackException# + |java/util/FormatFlagsConversionMismatchException# + |java/util/FormatterClosedException# + |java/util/IllegalFormatArgumentIndexException# + |java/util/IllegalFormatCodePointException# + |java/util/IllegalFormatConversionException# + |java/util/IllegalFormatException# + |java/util/IllegalFormatFlagsException# + |java/util/IllegalFormatPrecisionException# + |java/util/IllegalFormatWidthException# + |java/util/IllformedLocaleException# + |java/util/InputMismatchException# + |java/util/InvalidPropertiesFormatException# + |java/util/MissingFormatArgumentException# + |java/util/MissingFormatWidthException# + |java/util/MissingResourceException# + |java/util/NoSuchElementException# + |java/util/TooManyListenersException# + |java/util/UnknownFormatConversionException# + |java/util/UnknownFormatFlagsException# + |java/util/concurrent/BrokenBarrierException# + |java/util/concurrent/CancellationException# + |java/util/concurrent/CompletionException# + |java/util/concurrent/ExecutionException# + |java/util/concurrent/RejectedExecutionException# + |java/util/concurrent/TimeoutException# + |java/util/jar/JarException# + |java/util/prefs/BackingStoreException# + |java/util/prefs/InvalidPreferencesFormatException# + |java/util/regex/PatternSyntaxException# + |java/util/zip/DataFormatException# + |java/util/zip/ZipException# + |javax/annotation/processing/FilerException# + |javax/crypto/AEADBadTagException# + |javax/crypto/BadPaddingException# + |javax/crypto/ExemptionMechanismException# + |javax/crypto/IllegalBlockSizeException# + |javax/crypto/NoSuchPaddingException# + |javax/crypto/ShortBufferException# + |javax/imageio/IIOException# + |javax/imageio/metadata/IIODOMException# + |javax/imageio/metadata/IIOInvalidTreeException# + |javax/lang/model/UnknownEntityException# + |javax/lang/model/element/UnknownAnnotationValueException# + |javax/lang/model/element/UnknownDirectiveException# + |javax/lang/model/element/UnknownElementException# + |javax/lang/model/type/MirroredTypeException# + |javax/lang/model/type/MirroredTypesException# + |javax/lang/model/type/UnknownTypeException# + |javax/management/AttributeNotFoundException# + |javax/management/BadAttributeValueExpException# + |javax/management/BadBinaryOpValueExpException# + |javax/management/BadStringOperationException# + |javax/management/InstanceAlreadyExistsException# + |javax/management/InstanceNotFoundException# + |javax/management/IntrospectionException# + |javax/management/InvalidApplicationException# + |javax/management/InvalidAttributeValueException# + |javax/management/JMException# + |javax/management/JMRuntimeException# + |javax/management/ListenerNotFoundException# + |javax/management/MBeanException# + |javax/management/MBeanRegistrationException# + |javax/management/MalformedObjectNameException# + |javax/management/NotCompliantMBeanException# + |javax/management/OperationsException# + |javax/management/ReflectionException# + |javax/management/RuntimeErrorException# + |javax/management/RuntimeMBeanException# + |javax/management/RuntimeOperationsException# + |javax/management/ServiceNotFoundException# + |javax/management/modelmbean/InvalidTargetObjectTypeException# + |javax/management/modelmbean/XMLParseException# + |javax/management/monitor/MonitorSettingException# + |javax/management/openmbean/InvalidKeyException# + |javax/management/openmbean/InvalidOpenTypeException# + |javax/management/openmbean/KeyAlreadyExistsException# + |javax/management/openmbean/OpenDataException# + |javax/management/relation/InvalidRelationIdException# + |javax/management/relation/InvalidRelationServiceException# + |javax/management/relation/InvalidRelationTypeException# + |javax/management/relation/InvalidRoleInfoException# + |javax/management/relation/InvalidRoleValueException# + |javax/management/relation/RelationException# + |javax/management/relation/RelationNotFoundException# + |javax/management/relation/RelationServiceNotRegisteredException# + |javax/management/relation/RelationTypeNotFoundException# + |javax/management/relation/RoleInfoNotFoundException# + |javax/management/relation/RoleNotFoundException# + |javax/management/remote/JMXProviderException# + |javax/management/remote/JMXServerErrorException# + |javax/naming/AuthenticationException# + |javax/naming/AuthenticationNotSupportedException# + |javax/naming/CannotProceedException# + |javax/naming/CommunicationException# + |javax/naming/ConfigurationException# + |javax/naming/ContextNotEmptyException# + |javax/naming/InsufficientResourcesException# + |javax/naming/InterruptedNamingException# + |javax/naming/InvalidNameException# + |javax/naming/LimitExceededException# + |javax/naming/LinkException# + |javax/naming/LinkLoopException# + |javax/naming/MalformedLinkException# + |javax/naming/NameAlreadyBoundException# + |javax/naming/NameNotFoundException# + |javax/naming/NamingException# + |javax/naming/NamingSecurityException# + |javax/naming/NoInitialContextException# + |javax/naming/NoPermissionException# + |javax/naming/NotContextException# + |javax/naming/OperationNotSupportedException# + |javax/naming/PartialResultException# + |javax/naming/ReferralException# + |javax/naming/ServiceUnavailableException# + |javax/naming/SizeLimitExceededException# + |javax/naming/TimeLimitExceededException# + |javax/naming/directory/AttributeInUseException# + |javax/naming/directory/AttributeModificationException# + |javax/naming/directory/InvalidAttributeIdentifierException# + |javax/naming/directory/InvalidAttributeValueException# + |javax/naming/directory/InvalidAttributesException# + |javax/naming/directory/InvalidSearchControlsException# + |javax/naming/directory/InvalidSearchFilterException# + |javax/naming/directory/NoSuchAttributeException# + |javax/naming/directory/SchemaViolationException# + |javax/naming/ldap/LdapReferralException# + |javax/net/ssl/SSLException# + |javax/net/ssl/SSLHandshakeException# + |javax/net/ssl/SSLKeyException# + |javax/net/ssl/SSLPeerUnverifiedException# + |javax/net/ssl/SSLProtocolException# + |javax/print/PrintException# + |javax/print/attribute/UnmodifiableSetException# + |javax/script/ScriptException# + |javax/security/auth/DestroyFailedException# + |javax/security/auth/RefreshFailedException# + |javax/security/auth/callback/UnsupportedCallbackException# + |javax/security/auth/login/AccountException# + |javax/security/auth/login/AccountExpiredException# + |javax/security/auth/login/AccountLockedException# + |javax/security/auth/login/AccountNotFoundException# + |javax/security/auth/login/CredentialException# + |javax/security/auth/login/CredentialExpiredException# + |javax/security/auth/login/CredentialNotFoundException# + |javax/security/auth/login/FailedLoginException# + |javax/security/auth/login/LoginException# + |javax/security/cert/CertificateEncodingException# + |javax/security/cert/CertificateException# + |javax/security/cert/CertificateExpiredException# + |javax/security/cert/CertificateNotYetValidException# + |javax/security/cert/CertificateParsingException# + |javax/security/sasl/AuthenticationException# + |javax/security/sasl/SaslException# + |javax/smartcardio/CardException# + |javax/smartcardio/CardNotPresentException# + |javax/sound/midi/InvalidMidiDataException# + |javax/sound/midi/MidiUnavailableException# + |javax/sound/sampled/LineUnavailableException# + |javax/sound/sampled/UnsupportedAudioFileException# + |javax/sql/rowset/RowSetWarning# + |javax/sql/rowset/serial/SerialException# + |javax/sql/rowset/spi/SyncFactoryException# + |javax/sql/rowset/spi/SyncProviderException# + |javax/swing/UnsupportedLookAndFeelException# + |javax/swing/text/BadLocationException# + |javax/swing/text/ChangedCharSetException# + |javax/swing/tree/ExpandVetoException# + |javax/swing/undo/CannotRedoException# + |javax/swing/undo/CannotUndoException# + |""".stripMargin, + topLines = Some(500), + ) + } check( "multi-module", diff --git a/tests/unit/src/test/scala/tests/JavaToplevelSuite.scala b/tests/unit/src/test/scala/tests/JavaToplevelSuite.scala index 2d27c36d6d5..d01d058da76 100644 --- a/tests/unit/src/test/scala/tests/JavaToplevelSuite.scala +++ b/tests/unit/src/test/scala/tests/JavaToplevelSuite.scala @@ -1,15 +1,9 @@ package tests -import java.nio.file.Files +class JavaToplevelSuite extends BaseToplevelSuite { -import scala.meta.dialects -import scala.meta.internal.metals.MetalsEnrichments._ -import scala.meta.internal.mtags.Mtags -import scala.meta.io.AbsolutePath - -import munit._ - -class JavaToplevelSuite extends BaseSuite { + override def filename: String = "Test.java" + override def allowedModes: Set[Mode] = Set(Toplevel, ToplevelWithInner) check( "base", @@ -35,9 +29,7 @@ class JavaToplevelSuite extends BaseSuite { | | |""".stripMargin, - s"""|sample/pkg/Abc# - |sample/pkg/Enum# - |""".stripMargin, + List("sample/pkg/Abc#", "sample/pkg/Enum#"), ) check( @@ -49,8 +41,7 @@ class JavaToplevelSuite extends BaseSuite { | |} |""".stripMargin, - s"""|dot/clz/Abc# - |""".stripMargin, + List("dot/clz/Abc#"), ) check( @@ -61,8 +52,7 @@ class JavaToplevelSuite extends BaseSuite { | |} |""".stripMargin, - s"""|dot/record/Abc# - |""".stripMargin, + List("dot/record/Abc#"), ) check( @@ -73,26 +63,103 @@ class JavaToplevelSuite extends BaseSuite { | |} |""".stripMargin, - s"""|dot/enum/Abc# - |""".stripMargin, + List("dot/enum/Abc#"), + ) + + check( + "extends", + """|package dot.example; + | + |public class JavaClass extends Exception { + | + | private JavaClass() { + | + | } + | public JavaClass(int d) { + | this.d = d; + | } + | + | public static void a() { + | } + | + | public int b() { + | return 1; + | } + | + | public static int c = 2; + | public int d = 2; + | + | public class InnerClass { + | public int b() { + | return 1; + | } + | + | public int d = 2; + | } + | + | public static class InnerStaticClass implements InnerInterface { + | public static void a() { + | } + | + | public int b() { + | return 1; + | } + | + | public static int c = 2; + | public int d = 2; + | } + | + | public static interface InnerInterface { + | public static void a() { + | } + | + | public int b(); + | } + | + | public String publicName() { + | return "name"; + | } + | + | // Weird formatting + | @Override + | public String + | toString() { + | return ""; + | } + |} + |""".stripMargin, + List("dot/", "dot/example/", "dot/example/JavaClass# -> Exception", + "dot/example/JavaClass#InnerClass#", + "dot/example/JavaClass#InnerInterface#", + "dot/example/JavaClass#InnerStaticClass# -> InnerInterface"), + mode = ToplevelWithInner, ) - def check( - name: TestOptions, - code: String, - expected: String, - )(implicit loc: Location) { - test(name) { - val input = AbsolutePath(Files.createTempFile("mtags", ".java")) - input.writeText(code) - val obtained = - Mtags.topLevelSymbols(input, dialects.Scala213) + check( + "implements", + """|package example; + | + |public class ExampleClass { + | public static interface SomeInterface { } + | + | public static interface SomeOtherInterface { } + | + | public static class SomeClass extends SomeAbstractClass implements SomeInterface, SomeOtherInterface { + | public static class InnerClass implements SomeOtherInterface { } + | } + | + | public static abstract class SomeAbstractClass { } + |} + |""".stripMargin, + List( + "example/", "example/ExampleClass#", + "example/ExampleClass#SomeClass# -> SomeAbstractClass, SomeInterface, SomeOtherInterface", + "example/ExampleClass#SomeClass#InnerClass# -> SomeOtherInterface", + "example/ExampleClass#SomeAbstractClass#", + "example/ExampleClass#SomeInterface#", + "example/ExampleClass#SomeOtherInterface#", + ), + mode = ToplevelWithInner, + ) - assertNoDiff( - obtained.sorted.mkString("\n"), - expected, - ) - input.delete() - } - } } diff --git a/tests/unit/src/test/scala/tests/ScalaToplevelSuite.scala b/tests/unit/src/test/scala/tests/ScalaToplevelSuite.scala index 38e763c55ea..766b6c1f167 100644 --- a/tests/unit/src/test/scala/tests/ScalaToplevelSuite.scala +++ b/tests/unit/src/test/scala/tests/ScalaToplevelSuite.scala @@ -1,18 +1,8 @@ package tests -import java.nio.file.Files - -import scala.meta.Dialect import scala.meta.dialects -import scala.meta.internal.metals.MetalsEnrichments._ -import scala.meta.internal.mtags.Mtags -import scala.meta.internal.mtags.ResolvedOverriddenSymbol -import scala.meta.internal.mtags.UnresolvedOverriddenSymbol -import scala.meta.io.AbsolutePath - -import munit.TestOptions -class ScalaToplevelSuite extends BaseSuite { +class ScalaToplevelSuite extends BaseToplevelSuite { check( "basic-indented", @@ -690,68 +680,4 @@ class ScalaToplevelSuite extends BaseSuite { ), mode = All, ) - - def check( - options: TestOptions, - code: String, - expected: List[String], - mode: Mode = Toplevel, - dialect: Dialect = dialects.Scala3, - )(implicit location: munit.Location): Unit = { - test(options) { - val dir = AbsolutePath(Files.createTempDirectory("mtags")) - val input = dir.resolve("Test.scala") - input.writeText(code) - val obtained = - mode match { - case All | ToplevelWithInner => - val includeMembers = mode == All - val (doc, overrides) = - Mtags.indexWithOverrides(input, dialect, includeMembers) - val overriddenMap = overrides.toMap - doc.symbols.map { symbolInfo => - val symbol = symbolInfo.symbol - val suffix = if (symbolInfo.isExtension) " EXT" else "" - overriddenMap.get(symbol) match { - case None => s"$symbol$suffix" - case Some(symbols) => - val overridden = - symbols - .map { - case ResolvedOverriddenSymbol(symbol) => symbol - case UnresolvedOverriddenSymbol(name) => name - } - .mkString(", ") - s"$symbol$suffix -> $overridden" - } - } - case Toplevel => Mtags.topLevelSymbols(input, dialect) - } - input.delete() - dir.delete() - assertNoDiff( - obtained.sorted.mkString("\n"), - expected.sorted.mkString("\n"), - ) - } - } - - def assertToplevelsNoDiff( - obtained: List[String], - expected: List[String], - ): Unit = { - assertNoDiff(obtained.sorted.mkString("\n"), expected.sorted.mkString("\n")) - } - - sealed trait Mode - // includeInnerClasses = false - // includeMembers = false - case object Toplevel extends Mode - // includeInnerClasses = true - // includeMembers = false - case object ToplevelWithInner extends Mode - // includeInnerClasses = true - // includeMembers = true - case object All extends Mode - }