diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/Params.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/Params.scala new file mode 100644 index 00000000000..069e9c0e40f --- /dev/null +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/Params.scala @@ -0,0 +1,27 @@ +package scala.meta.internal.pc + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Flags._ + +case class Params( + labels: Seq[String], + kind: Params.Kind +) + +object Params { + enum Kind { + case TypeParameter, Normal, Implicit, Using + } + + def paramsKind(syms: List[Symbol])(using Context): Params.Kind = { + syms match { + case head :: _ => + if (head.isType) Kind.TypeParameter + else if (head.is(Given)) Kind.Using + else if (head.is(Implicit)) Kind.Implicit + else Kind.Normal + case Nil => Kind.Normal + } + } +} diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala index 64ab052dc06..3ef5ff7e5e9 100644 --- a/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala @@ -134,14 +134,24 @@ case class ScalaPresentationCompiler( CompletionProvider(pos, ctx.fresh.setCompilationUnit(unit)) .completions() val metalsCompletions = completionPosition(pos, path)(using ctx) - completions ++ metalsCompletions + + val newctx = ctx.fresh.setCompilationUnit(unit) + val tpdPath = + Interactive.pathTo(newctx.compilationUnit.tpdTree, pos.span)(using + newctx + ) + val locatedCtx = Interactive.contextOfPath(tpdPath)(using newctx) + val history = ShortenedNames(locatedCtx) + + (completions ++ metalsCompletions).zipWithIndex.flatMap { + case (item, idx) => + completionItems(item, history, idx)(using newctx) + } case None => Nil } + new CompletionList( - /*isIncomplete = */ false, - items.zipWithIndex.flatMap { case (item, idx) => - completionItems(item, idx)(using ctx) - }.asJava + items.asJava ) } } @@ -164,7 +174,6 @@ case class ScalaPresentationCompiler( "", definitions.flatMap(d => location(d.namePos(using ctx))).asJava ) - } } @@ -272,22 +281,20 @@ case class ScalaPresentationCompiler( case _: ImportType => printer.typeString(symbol.paramRef(using ctx)) case _ => - val shortendType = - driver.compilationUnits.get(uri) match { - case Some(unit) => - val newctx = - ctx.fresh.setCompilationUnit(unit) - val path = Interactive.pathTo( - newctx.compilationUnit.tpdTree, - pos.span - )(using newctx) - val context = - Interactive.contextOfPath(path)(using newctx) - val history = ShortenedNames(context) - shortType(tpw, history)(using newctx) - case None => tpw - } - printer.typeString(shortendType) + driver.compilationUnits.get(uri) match { + case Some(unit) => + val newctx = + ctx.fresh.setCompilationUnit(unit) + val tpdPath = Interactive.pathTo( + newctx.compilationUnit.tpdTree, + pos.span + )(using newctx) + val context = + Interactive.contextOfPath(tpdPath)(using newctx) + val history = new ShortenedNames(context) + printer.infoString(symbol, history, tpw)(using context) + case None => printer.typeString(tpw) + } } } val content = hoverContent( @@ -424,8 +431,29 @@ case class ScalaPresentationCompiler( private def completionItems( completion: Completion, + history: ShortenedNames, idx: Int - )(using ctx: Context): List[CompletionItem] = { + )(using Context): List[CompletionItem] = { + val printer = SymbolPrinter()(using ctx) + + /** + * Calculate the string for "detail" field in CompletionItem. + * + * for class or module, it's package name that it belongs to (e.g. "scala.collection" for "scala.collection.Seq") + * otherwise, it's shortened type/method signature + * e.g. "[A: Ordering](x: List[Int]): A", " java.lang.String" + * + * @param sym The symbol for completion item. + */ + def detailString(sym: Symbol): String = { + if (sym.isClass || sym.is(Module)) { + val printer = SymbolPrinter() + s" ${printer.fullNameString(sym.owner)}" + } else { + printer.infoString(sym, history, sym.info.widenTermRefExpr)(using ctx) + } + } + def completionItemKind( sym: Symbol )(using ctx: Context): CompletionItemKind = { @@ -442,42 +470,42 @@ case class ScalaPresentationCompiler( else CompletionItemKind.Field } - val printer = SymbolPrinter()(using ctx) + completion.symbols.map { sym => // For overloaded signatures we get multiple symbols, so we need // to recalculate the description // related issue https://github.com/lampepfl/dotty/issues/11941 - val description = - if (completion.symbols.size > 1) - if (sym.isType) printer.fullNameString(sym) - else { - printer.typeString(sym.denot.info.widenTermRefExpr) - } - else - completion.description.stripSuffix("$") + lazy val kind: CompletionItemKind = completionItemKind(sym) + + val description = detailString(sym) + + val ident = completion.label + val label = kind match { + case CompletionItemKind.Method => + s"${ident}${description}" + case CompletionItemKind.Variable | CompletionItemKind.Field => + s"${ident}: ${description}" + case _ => ident + } - val colonNotNeeded = sym.is(Method) - val colon = if (colonNotNeeded) "" else ": " - val label = s"${completion.label}$colon${description}" val item = new CompletionItem(label) item.setSortText(f"${idx}%05d") + item.setDetail(description) item.setFilterText(completion.label) // TODO we should use edit text item.setInsertText(completion.label) val documentation = ParsedComment.docOf(sym) + if (documentation.nonEmpty) { - item.setDocumentation( - hoverContent(None, None, documentation.toList) - ) + item.setDocumentation(hoverContent(None, None, documentation.toList)) } if (sym.isDeprecated) { item.setTags(List(CompletionItemTag.Deprecated).asJava) } item.setKind(completionItemKind(sym)) item - } } @@ -546,134 +574,4 @@ case class ScalaPresentationCompiler( } override def isLoaded() = compilerAccess.isLoaded() - - private case class ShortName( - name: Name, - symbol: Symbol - ) - private object ShortName { - def apply(sym: Symbol)(using ctx: Context): ShortName = - ShortName(sym.name, sym) - } - - private class ShortenedNames(context: Context) { - val history = collection.mutable.Map.empty[Name, ShortName] - - def lookupSymbol(short: ShortName): Type = { - context.findRef(short.name) - } - - def tryShortenName(short: ShortName)(using Context): Boolean = { - history.get(short.name) match { - case Some(ShortName(_, other)) => true - case None => - val foundTpe = lookupSymbol(short) - val syms = List(foundTpe.termSymbol, foundTpe.typeSymbol) - val isOk = syms.filter(_ != NoSymbol) match { - case Nil => false - case founds => founds.exists(_ == short.symbol) - } - if (isOk) { - history(short.name) = short - true - } else { - false - } - } - } - - def tryShortenName(name: Option[ShortName])(using Context): Boolean = - name match { - case Some(short) => tryShortenName(short) - case None => false - } - } - - /** - * Shorten the long (fully qualified) type to shorter representation, so printers - * can obtain more readable form of type like `SrcPos` instead of `dotc.util.SrcPos` - * (if the name can be resolved from the context). - * - * For example, - * when the longType is like `TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class dotc)),module util),SrcPos)`, - * if `dotc.util.SrcPos` found from the scope, then `TypeRef(NoPrefix, SrcPos)` - * if not, and `dotc.util` found from the scope then `TypeRef(TermRef(NoPrefix, module util), SrcPos)` - * - * @see Scala 3/Internals/Type System https://dotty.epfl.ch/docs/internals/type-system.html - */ - private def shortType(longType: Type, history: ShortenedNames)(using - ctx: Context - ): Type = { - val isVisited = collection.mutable.Set.empty[(Type, Option[ShortName])] - val cached = new ju.HashMap[(Type, Option[ShortName]), Type]() - - def loop(tpe: Type, name: Option[ShortName]): Type = { - val key = tpe -> name - // NOTE: Prevent infinite recursion, see https://github.com/scalameta/metals/issues/749 - if (isVisited(key)) return cached.getOrDefault(key, tpe) - isVisited += key - val result = tpe match { - case TypeRef(prefix, designator) => - // designator is not necessarily an instance of `Symbol` and it's an instance of `Name` - // this can be seen, for example, when we are shortening the signature of 3rd party APIs. - val sym = - if (designator.isInstanceOf[Symbol]) - designator.asInstanceOf[Symbol] - else tpe.typeSymbol - val short = ShortName(sym) - TypeRef(loop(prefix, Some(short)), sym) - - case TermRef(prefix, designator) => - val sym = - if (designator.isInstanceOf[Symbol]) - designator.asInstanceOf[Symbol] - else tpe.termSymbol - val short = ShortName(sym) - if (history.tryShortenName(name)) NoPrefix - else TermRef(loop(prefix, None), sym) - - case ThisType(tyref) => - if (history.tryShortenName(name)) NoPrefix - else ThisType.raw(loop(tyref, None).asInstanceOf[TypeRef]) - - case mt @ MethodTpe(pnames, ptypes, restpe) if mt.isImplicitMethod => - ImplicitMethodType( - pnames, - ptypes.map(loop(_, None)), - loop(restpe, None) - ) - case mt @ MethodTpe(pnames, ptypes, restpe) => - MethodType(pnames, ptypes.map(loop(_, None)), loop(restpe, None)) - - case pl @ PolyType(_, restpe) => - PolyType( - pl.paramNames, - pl.paramInfos.map(bound => - TypeBounds(loop(bound.lo, None), loop(bound.hi, None)) - ), - loop(restpe, None) - ) - case ConstantType(value) => value.tpe - case SuperType(thistpe, supertpe) => - SuperType(loop(thistpe, None), loop(supertpe, None)) - case AppliedType(tycon, args) => - AppliedType(loop(tycon, None), args.map(a => loop(a, None))) - case TypeBounds(lo, hi) => - TypeBounds(loop(lo, None), loop(hi, None)) - case ExprType(res) => - ExprType(loop(res, None)) - case AnnotatedType(parent, annot) => - AnnotatedType(loop(parent, None), annot) - case AndType(tp1, tp2) => - AndType(loop(tp1, None), loop(tp2, None)) - case or @ OrType(tp1, tp2) => - OrType(loop(tp1, None), loop(tp2, None), or.isSoft) - case t => t - } - - cached.putIfAbsent(key, result) - result - } - loop(longType, None) - } } diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/Signatures.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/Signatures.scala new file mode 100644 index 00000000000..48e42722cb2 --- /dev/null +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/Signatures.scala @@ -0,0 +1,143 @@ +package scala.meta.internal.pc + +import java.{util => ju} + +import collection.mutable.ListBuffer +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.NameKinds.EvidenceParamName +import scala.meta.internal.mtags.MtagsEnrichments._ + +import dotty.tools.dotc.core.Flags._ + +class ShortenedNames(context: Context) { + val history = collection.mutable.Map.empty[Name, ShortName] + + def lookupSymbol(short: ShortName): Type = { + context.findRef(short.name) + } + + def tryShortenName(short: ShortName)(using Context): Boolean = { + history.get(short.name) match { + case Some(ShortName(_, other)) => true + case None => + val foundTpe = lookupSymbol(short) + val syms = List(foundTpe.termSymbol, foundTpe.typeSymbol) + val isOk = syms.filter(_ != NoSymbol) match { + case Nil => false + case founds => founds.exists(_ == short.symbol) + } + if (isOk) { + history(short.name) = short + true + } else { + false + } + } + } + + def tryShortenName(name: Option[ShortName])(using Context): Boolean = + name match { + case Some(short) => tryShortenName(short) + case None => false + } +} + +case class ShortName( + name: Name, + symbol: Symbol +) +object ShortName { + def apply(sym: Symbol)(using ctx: Context): ShortName = + ShortName(sym.name, sym) +} + +/** + * Shorten the long (fully qualified) type to shorter representation, so printers + * can obtain more readable form of type like `SrcPos` instead of `dotc.util.SrcPos` + * (if the name can be resolved from the context). + * + * For example, + * when the longType is like `TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class dotc)),module util),SrcPos)`, + * if `dotc.util.SrcPos` found from the scope, then `TypeRef(NoPrefix, SrcPos)` + * if not, and `dotc.util` found from the scope then `TypeRef(TermRef(NoPrefix, module util), SrcPos)` + * + * @see Scala 3/Internals/Type System https://dotty.epfl.ch/docs/internals/type-system.html + */ +def shortType(longType: Type, history: ShortenedNames)(using + ctx: Context +): Type = { + val isVisited = collection.mutable.Set.empty[(Type, Option[ShortName])] + val cached = new ju.HashMap[(Type, Option[ShortName]), Type]() + + def loop(tpe: Type, name: Option[ShortName]): Type = { + val key = tpe -> name + // NOTE: Prevent infinite recursion, see https://github.com/scalameta/metals/issues/749 + if (isVisited(key)) return cached.getOrDefault(key, tpe) + isVisited += key + val result = tpe match { + case TypeRef(prefix, designator) => + // designator is not necessarily an instance of `Symbol` and it's an instance of `Name` + // this can be seen, for example, when we are shortening the signature of 3rd party APIs. + val sym = + if (designator.isInstanceOf[Symbol]) + designator.asInstanceOf[Symbol] + else tpe.typeSymbol + val short = ShortName(sym) + TypeRef(loop(prefix, Some(short)), sym) + + case TermRef(prefix, designator) => + val sym = + if (designator.isInstanceOf[Symbol]) + designator.asInstanceOf[Symbol] + else tpe.termSymbol + val short = ShortName(sym) + if (history.tryShortenName(name)) NoPrefix + else TermRef(loop(prefix, None), sym) + + case ThisType(tyref) => + if (history.tryShortenName(name)) NoPrefix + else ThisType.raw(loop(tyref, None).asInstanceOf[TypeRef]) + + case mt @ MethodTpe(pnames, ptypes, restpe) if mt.isImplicitMethod => + ImplicitMethodType( + pnames, + ptypes.map(loop(_, None)), + loop(restpe, None) + ) + case mt @ MethodTpe(pnames, ptypes, restpe) => + MethodType(pnames, ptypes.map(loop(_, None)), loop(restpe, None)) + + case pl @ PolyType(_, restpe) => + PolyType( + pl.paramNames, + pl.paramInfos.map(bound => + TypeBounds(loop(bound.lo, None), loop(bound.hi, None)) + ), + loop(restpe, None) + ) + case ConstantType(value) => value.tpe + case SuperType(thistpe, supertpe) => + SuperType(loop(thistpe, None), loop(supertpe, None)) + case AppliedType(tycon, args) => + AppliedType(loop(tycon, None), args.map(a => loop(a, None))) + case TypeBounds(lo, hi) => + TypeBounds(loop(lo, None), loop(hi, None)) + case ExprType(res) => + ExprType(loop(res, None)) + case AnnotatedType(parent, annot) => + AnnotatedType(loop(parent, None), annot) + case AndType(tp1, tp2) => + AndType(loop(tp1, None), loop(tp2, None)) + case or @ OrType(tp1, tp2) => + OrType(loop(tp1, None), loop(tp2, None), or.isSoft) + case t => t + } + + cached.putIfAbsent(key, result) + result + } + loop(longType, None) +} diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/SymbolPrinter.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/SymbolPrinter.scala index 1aa3cb4ed65..72e4f33c855 100644 --- a/mtags/src/main/scala-3/scala/meta/internal/pc/SymbolPrinter.scala +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/SymbolPrinter.scala @@ -1,5 +1,7 @@ package scala.meta.internal.pc +import collection.mutable.ListBuffer +import dotty.tools.dotc.core.NameKinds.EvidenceParamName import dotty.tools.dotc.printing.RefinedPrinter import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Symbols._ @@ -13,7 +15,7 @@ class SymbolPrinter(using ctx: Context) extends RefinedPrinter(ctx) { private val defaultWidth = 1000 override def nameString(name: Name): String = { - name.stripModuleClassSuffix.toString() + super.nameString(name.stripModuleClassSuffix) } def typeString(tpw: Type): String = { @@ -21,27 +23,179 @@ class SymbolPrinter(using ctx: Context) extends RefinedPrinter(ctx) { } def fullDefinition(sym: Symbol, tpe: Type): String = { - - def isNullary = - tpe match { - case tpe: (MethodType | PolyType) => - false - case other => - true - } - val isImplicit = sym.is(Flags.Implicit) val name = nameString(sym) val implicitKeyword = if (isImplicit) "implicit " else "" keyString(sym) match { - case key @ ("var" | "val") => - s"$key $name: " case "" => s"$implicitKeyword$name: " - case key if isNullary => - s"$key $name: " - case key => + case key if sym.is(Flags.Method) => s"$key $name" + case key => + s"$key $name: " + } + } + + /** + * for + * - method: method signature + * - e.g. `[A: Ordering](x: List[Int]): A` + * - otherwise: its shortened type + * - e.g. ` java.lang.String` ` Symbols.Symbol` + */ + def infoString( + sym: Symbol, + history: ShortenedNames, + info: Type + )(using Context): String = { + sym match { + case m if m.is(Flags.Method) => + defaultMethodSignature(sym, history, info) + case _ => + val short = shortType(info, history) + s"${typeString(short)}" + } + } + + /** + * Compute method signature for the given (method) symbol. + * + * @return shortend name for types or the type for terms + * e.g. "[A: Ordering](a: A, b: B): collection.mutable.Map[A, B]" + * ": collection.mutable.Map[A, B]" for no-arg method + */ + def defaultMethodSignature( + gsym: Symbol, + shortenedNames: ShortenedNames, + gtpe: Type + ): String = { + // In case rawParamss is no set, fallback to paramSymss + val paramss = + if (gsym.rawParamss.length != 0) gsym.rawParamss else gsym.paramSymss + lazy val implicitParams: List[Symbol] = + paramss.flatMap(params => params.filter(p => p.is(Flags.Implicit))) + + lazy val implicitEvidenceParams: Set[Symbol] = + implicitParams + .filter(p => p.name.toString.startsWith(EvidenceParamName.separator)) + .toSet + + lazy val implicitEvidencesByTypeParam: Map[Symbol, List[String]] = + constructImplicitEvidencesByTypeParam( + implicitEvidenceParams.toList, + shortenedNames + ) + + val paramLabelss = paramss.flatMap { params => + val labels = params.flatMap { param => + // Don't show implicit evidence params + // e.g. + // from [A: Ordering](a: A, b: A)(implicit evidence$1: Ordering[A]) + // to [A: Ordering](a: A, b: A): A + if (implicitEvidenceParams.contains(param)) Nil + else + paramLabel(param, implicitEvidencesByTypeParam, shortenedNames) :: Nil + } + + // Remove empty params + if (labels.isEmpty) Nil + else labels.iterator :: Nil + }.iterator + + val returnType = + typeString(shortType(gtpe.finalResultType, shortenedNames)) + methodSignature(paramLabelss, paramss, returnType) + } + + private def methodSignature( + paramLabels: Iterator[Iterator[String]], + paramss: List[List[Symbol]], + returnType: String + )(using Context) = { + paramLabels + .zip(paramss) + .map { case (params, syms) => + Params.paramsKind(syms) match { + case Params.Kind.TypeParameter => + params.mkString("[", ", ", "]") + case Params.Kind.Normal => + params.mkString("(", ", ", ")") + case Params.Kind.Using => + params.mkString( + "(using ", + ", ", + ")" + ) + case Params.Kind.Implicit => + params.mkString( + "(implicit ", + ", ", + ")" + ) + } + } + .mkString("", "", s": ${returnType}") + } + + /** + * Construct param (both value params and type params) label string (e.g. "param1: TypeOfParam", "A: Ordering") + * for the given parameter's symbol. + */ + private def paramLabel( + param: Symbol, + implicitEvidences: Map[Symbol, List[String]], + shortenedNames: ShortenedNames + )(using Context): String = { + val keywordName = nameString(param) + val paramTypeString = typeString( + shortType(param.info, shortenedNames) + ) + if (param.isTypeParam) { + // pretty context bounds + // e.g. f[A](a: A, b: A)(implicit evidence$1: Ordering[A]) + // to f[A: Ordering](a: A, b: A)(implicit evidence$1: Ordering[A]) + val bounds = implicitEvidences.getOrElse(param, Nil) match { + case Nil => "" + case head :: Nil => s": $head" + case many => many.mkString(": ", ": ", "") + } + s"$keywordName$paramTypeString$bounds" + } else if (param.is(Flags.Given) && param.name.toString.contains('$')) { + // For Anonymous Context Parameters + // print only type string + // e.g. "using Ord[T]" instead of "using x$0: Ord[T]" + paramTypeString + } else { + val paramTypeString = typeString( + shortType(param.info, shortenedNames) + ) + s"${keywordName}: ${paramTypeString}" } } + + /** + * Create a mapping from type parameter symbol to its context bound string representations. + * + * @param implicitEvidenceParams - implicit evidence params (e.g. evidence$1: Ordering[A]) + * @return mapping from type param to its context bounds (e.g. Map(A -> List("Ordering")) ) + */ + private def constructImplicitEvidencesByTypeParam( + implicitEvidenceParams: List[Symbol], + shortenedNames: ShortenedNames + ): Map[Symbol, List[String]] = { + val result = collection.mutable.Map.empty[Symbol, ListBuffer[String]] + implicitEvidenceParams.iterator + .map(_.info) + .collect { + // AppliedType(TypeRef(ThisType(TypeRef(NoPrefix,module class reflect)),trait ClassTag),List(TypeRef(NoPrefix,type T))) + case AppliedType(tycon, TypeRef(_, tparam) :: Nil) + if tparam.isInstanceOf[Symbol] => + (tycon, tparam.asInstanceOf[Symbol]) + } + .foreach { case (tycon, tparam) => + val buf = result.getOrElseUpdate(tparam, ListBuffer.empty[String]) + buf += typeString(shortType(tycon, shortenedNames)) + } + result.map(kv => (kv._1, kv._2.toList)).toMap + } } diff --git a/tests/cross/src/main/scala/tests/BasePCSuite.scala b/tests/cross/src/main/scala/tests/BasePCSuite.scala index 169ae1af519..f8a6954a138 100644 --- a/tests/cross/src/main/scala/tests/BasePCSuite.scala +++ b/tests/cross/src/main/scala/tests/BasePCSuite.scala @@ -241,6 +241,9 @@ abstract class BasePCSuite extends BaseSuite { } } + object IgnoreScala2 + extends IgnoreScalaVersion(BuildInfoVersions.scala2Versions.toSet) + object IgnoreScala3 extends IgnoreScalaVersion(BuildInfoVersions.scala3Versions.toSet) diff --git a/tests/cross/src/test/scala/tests/hover/HoverDefnSuite.scala b/tests/cross/src/test/scala/tests/hover/HoverDefnSuite.scala index a810083d7ea..9b10abba731 100644 --- a/tests/cross/src/test/scala/tests/hover/HoverDefnSuite.scala +++ b/tests/cross/src/test/scala/tests/hover/HoverDefnSuite.scala @@ -51,10 +51,7 @@ class HoverDefnSuite extends BaseHoverSuite { |} |""".stripMargin, """|def empty[T]: Option[T] - |""".stripMargin.hover, - compat = Map( - "3.0" -> "def empty[T] => Option[T]".hover - ) + |""".stripMargin.hover ) check( @@ -66,10 +63,7 @@ class HoverDefnSuite extends BaseHoverSuite { """ |Option[T] |def empty[T: Ordering]: Option[T] - |""".stripMargin.hover, - compat = Map( - "3.0" -> "def empty[T](implicit evidence$1: Ordering[T]): Option[T]".hover - ) + |""".stripMargin.hover ) check( diff --git a/tests/cross/src/test/scala/tests/hover/HoverDocSuite.scala b/tests/cross/src/test/scala/tests/hover/HoverDocSuite.scala index e5d45bf9aa3..3205aefbd2e 100644 --- a/tests/cross/src/test/scala/tests/hover/HoverDocSuite.scala +++ b/tests/cross/src/test/scala/tests/hover/HoverDocSuite.scala @@ -46,7 +46,7 @@ class HoverDocSuite extends BaseHoverSuite { |List s = Collections.emptyList(); |``` |""".stripMargin, - "3.0" -> "def emptyList[T](): java.util.List[T]".hover + "3.0" -> "def emptyList[T]: java.util.List[T]".hover ) ) diff --git a/tests/cross/src/test/scala/tests/hover/HoverTermSuite.scala b/tests/cross/src/test/scala/tests/hover/HoverTermSuite.scala index 4e18fb86b84..46d0dc7443d 100644 --- a/tests/cross/src/test/scala/tests/hover/HoverTermSuite.scala +++ b/tests/cross/src/test/scala/tests/hover/HoverTermSuite.scala @@ -14,7 +14,7 @@ class HoverTermSuite extends BaseHoverSuite { |final override def map[B, That](f: Int => B)(implicit bf: CanBuildFrom[List[Int],B,That]): That |""".stripMargin.hover, compat = Map( - "3.0" -> "def map[B](f: Int => B): List[B]".hover + "3.0" -> "def map[B](f: A => B): List[B]".hover ) ) @@ -42,7 +42,7 @@ class HoverTermSuite extends BaseHoverSuite { """|List[Int] |def apply[A](elems: A*): List[A] |""".stripMargin.hover, - "3.0" -> "def apply: Int".hover + "3.0" -> "def apply[A](elems: A*): Int".hover ) ) @@ -126,7 +126,7 @@ class HoverTermSuite extends BaseHoverSuite { |def apply[T](a: T)(implicit ev: Int): T |""".stripMargin.hover, compat = Map( - "3.0" -> "def apply: Int".hover + "3.0" -> "def apply[T](a: T)(implicit ev: Int): Int".hover ) ) @@ -283,7 +283,7 @@ class HoverTermSuite extends BaseHoverSuite { |def flatMap[B](f: Int => Option[B]): Option[B] |""".stripMargin.hover, compat = Map( - "3.0" -> "def flatMap: Option[String]".hover + "3.0" -> "def flatMap[B](f: A => Option[B]): Option[String]".hover ) ) diff --git a/tests/cross/src/test/scala/tests/pc/CompletionSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionSuite.scala index d7d0b089c54..9cf5f4a5db9 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionSuite.scala @@ -27,7 +27,7 @@ class CompletionSuite extends BaseCompletionSuite { |List - java.util |JList - javax.swing |""".stripMargin, - "3.0" -> "List=> collection.immutable.List.type" + "3.0" -> "List: collection.immutable.List.type" ), topLines = Some(5) ) @@ -40,10 +40,7 @@ class CompletionSuite extends BaseCompletionSuite { |}""".stripMargin, """ |empty[A]: List[A] - |""".stripMargin, - compat = Map( - "3.0" -> "empty[A] => List[A]" - ) + |""".stripMargin ) check( @@ -56,6 +53,7 @@ class CompletionSuite extends BaseCompletionSuite { |""".stripMargin ) + // TODO: scala3, compute type parameters based on the qualifier. check( "tparam", """ @@ -68,11 +66,11 @@ class CompletionSuite extends BaseCompletionSuite { """|identity[B >: Int](a: B): B |""".stripMargin, compat = Map( - "3.0.0-RC1" -> "identity[B >: A](a: B): B", - "3.0" -> "identity[B >: Int](a: B): B" + "3.0" -> "identity[B >: A](a: B): B" ) ) + // TODO: scala3, compute type parameters based on the qualifier. check( "tparam1", """ @@ -85,11 +83,11 @@ class CompletionSuite extends BaseCompletionSuite { """|identity(a: Int): Int |""".stripMargin, compat = Map( - "3.0.0-RC1" -> "identity(a: A): A", - "3.0" -> "identity(a: Int): Int" + "3.0" -> "identity(a: A): A" ) ) + // TODO: scala3, compute type parameters based on the qualifier. check( "tparam2", """ @@ -101,8 +99,7 @@ class CompletionSuite extends BaseCompletionSuite { |""".stripMargin, compat = Map( "2.11" -> "getOrElse[B1 >: String](key: Int, default: => B1): B1", - "3.0.0-RC1" -> "getOrElse[V1 >: V](key: K, default: => V1): V1", - "3.0" -> "getOrElse[V1 >: String](key: Int, default: => V1): V1" + "3.0" -> "getOrElse[V1 >: V](key: K, default: => V1): V1" ) ) @@ -247,7 +244,7 @@ class CompletionSuite extends BaseCompletionSuite { """|XtensionMethod(a: Int): A.XtensionMethod |""".stripMargin, compat = Map( - "3.0" -> "XtensionMethod(a: Int): implicit-class.A.XtensionMethod" + "3.0" -> "XtensionMethod(a: Int): XtensionMethod" ) ) @@ -300,7 +297,7 @@ class CompletionSuite extends BaseCompletionSuite { """|TrieMap scala.collection.concurrent |TrieMapSerializationEnd - scala.collection.concurrent |""".stripMargin, - "3.0" -> "TrieMap: scala.collection.concurrent.TrieMap" + "3.0" -> "TrieMap scala.collection.concurrent" ) ) @@ -310,10 +307,7 @@ class CompletionSuite extends BaseCompletionSuite { |import scala.collection.conc@@ |""".stripMargin, """|concurrent scala.collection - |""".stripMargin, - compat = Map( - "3.0" -> "concurrent: scala.collection.concurrent" - ) + |""".stripMargin ) check( @@ -425,7 +419,7 @@ class CompletionSuite extends BaseCompletionSuite { |""".stripMargin, "", compat = Map( - "3.0" -> "Inner: a.Outer.Inner", + "3.0" -> "Inner a.Outer", /* TODO seems that changes in 2.13.5 made this pop up and this * might have been a bug in presentation compiler that we were using * https://github.com/scalameta/metals/issues/2546 @@ -452,7 +446,7 @@ class CompletionSuite extends BaseCompletionSuite { |Files - a.Outer |""".stripMargin, compat = Map( - "3.0" -> "Files: java.nio.file.Files" + "3.0" -> "Files java.nio.file" ) ) @@ -470,7 +464,7 @@ class CompletionSuite extends BaseCompletionSuite { includeCommitCharacter = true, compat = Map( "2.11" -> "empty[A, B]: Map[A,B] (commit: '')", - "3.0" -> "empty[K, V] => Map[K, V] (commit: '')" + "3.0" -> "empty[K, V]: Map[K, V] (commit: '')" // space between K V ) ) @@ -528,31 +522,31 @@ class CompletionSuite extends BaseCompletionSuite { topLines = Some(25), compat = Map( "3.0" -> - """|Function0: scala.Function0 - |Function1: scala.Function1 - |Function1: Function1 - |Function2: scala.Function2 - |Function3: scala.Function3 - |Function4: scala.Function4 - |Function5: scala.Function5 - |Function6: scala.Function6 - |Function7: scala.Function7 - |Function8: scala.Function8 - |Function9: scala.Function9 - |Function10: scala.Function10 - |Function11: scala.Function11 - |Function12: scala.Function12 - |Function13: scala.Function13 - |Function14: scala.Function14 - |Function15: scala.Function15 - |Function16: scala.Function16 - |Function17: scala.Function17 - |Function18: scala.Function18 - |Function19: scala.Function19 - |Function20: scala.Function20 - |Function21: scala.Function21 - |Function22: scala.Function22 - |Function: Function + """|Function0 scala + |Function1 scala + |Function1 scala + |Function2 scala + |Function3 scala + |Function4 scala + |Function5 scala + |Function6 scala + |Function7 scala + |Function8 scala + |Function9 scala + |Function10 scala + |Function11 scala + |Function12 scala + |Function13 scala + |Function14 scala + |Function15 scala + |Function16 scala + |Function17 scala + |Function18 scala + |Function19 scala + |Function20 scala + |Function21 scala + |Function22 scala + |Function scala |""".stripMargin ) ) @@ -567,7 +561,10 @@ class CompletionSuite extends BaseCompletionSuite { """|toCharArray(): Array[Char] |""".stripMargin, compat = Map( - "2.11" -> "" // SAM was introduced in Scala 2.12 + "2.11" -> "", // SAM was introduced in Scala 2.12 + "3.0" -> + """|toCharArray: Array[Char] + |""".stripMargin ) ) @@ -581,10 +578,24 @@ class CompletionSuite extends BaseCompletionSuite { """|concat[T: ClassTag](xss: Array[T]*): Array[T] |""".stripMargin, compat = Map( - "3.0" -> "concat[T](xss: Array[T]*)(implicit evidence$11: scala.reflect.ClassTag[T]): Array[T]" + "3.0" -> "concat[T: scala.reflect.ClassTag](xss: Array[T]*): Array[T]" ) ) + check( + "implicit-evidence-many", + """ + |object A { + | object B { + | def test[T: Ordering: Numeric](x: T): T = ??? + | } + | B.tes@@ + |} + """.stripMargin, + """|test[T: Ordering: Numeric](x: T): T + |""".stripMargin + ) + check( "bounds", """ @@ -597,11 +608,10 @@ class CompletionSuite extends BaseCompletionSuite { |""".stripMargin, compat = Map( "3.0.0-RC1" -> - """|readAttributes(x$0: java.nio.file.Path, x$1: String, x$2: java.nio.file.LinkOption*): - | java.util.Map[String, Object] + """|readAttributes(x$0: java.nio.file.Path, x$1: String, x$2: java.nio.file.LinkOption*): java.util.Map[String, Object] |""".stripMargin, "3.0" -> - """|readAttributes(x$0: java.nio.file.Path, x$1: String, x$2: java.nio.file.LinkOption*): java.util.Map[String, ] + """|readAttributes(x$0: java.nio.file.Path, x$1: String, x$2: java.nio.file.LinkOption*): java.util.Map[String, Object] |readAttributes[A <: java.nio.file.attribute.BasicFileAttributes](x$0: java.nio.file.Path, x$1: Class[A], x$2: java.nio.file.LinkOption*): A |""".stripMargin ) @@ -638,7 +648,7 @@ class CompletionSuite extends BaseCompletionSuite { """|DelayedLazyVal scala.concurrent |""".stripMargin, compat = Map( - "3.0" -> "DelayedLazyVal: scala.concurrent.DelayedLazyVal", + "3.0" -> "DelayedLazyVal scala.concurrent", "2.13.5" -> "DelayedLazyVal - scala.concurrent" ) ) @@ -674,7 +684,7 @@ class CompletionSuite extends BaseCompletionSuite { """|incrementThisType(): A.this.type (with underlying type singleton.A) |""".stripMargin, compat = Map( - "3.0" -> "incrementThisType(): (A.this : singleton.A)" + "3.0" -> "incrementThisType: (A.this : singleton.A)" ) ) @@ -739,7 +749,7 @@ class CompletionSuite extends BaseCompletionSuite { """|banana: Int |""".stripMargin, compat = Map( - "3.0" -> "selectDynamic(field: String): dynamic.Foo" + "3.0" -> "selectDynamic(field: String): Foo" ) ) @@ -753,7 +763,7 @@ class CompletionSuite extends BaseCompletionSuite { """|banana: Int |""".stripMargin, compat = Map( - "3.0" -> "selectDynamic(field: String): dynamic2.Foo" + "3.0" -> "selectDynamic(field: String): Foo" ) ) @@ -768,7 +778,7 @@ class CompletionSuite extends BaseCompletionSuite { """|banana: Int |""".stripMargin, compat = Map( - "3.0" -> "selectDynamic(field: String): dynamic3.Foo" + "3.0" -> "selectDynamic(field: String): Foo" ) ) @@ -782,7 +792,7 @@ class CompletionSuite extends BaseCompletionSuite { """|banana: Int |""".stripMargin, compat = Map( - "3.0" -> "selectDynamic(field: String): dynamic4.Foo" + "3.0" -> "selectDynamic(field: String): Foo" ) ) @@ -797,7 +807,7 @@ class CompletionSuite extends BaseCompletionSuite { """|selectDynamic(field: String): Foo |""".stripMargin, compat = Map( - "3.0" -> "selectDynamic(field: String): dynamic5.Foo" + "3.0" -> "selectDynamic(field: String): Foo" ) ) @@ -847,10 +857,7 @@ class CompletionSuite extends BaseCompletionSuite { |} |""".stripMargin, """|Some scala - |""".stripMargin, - compat = Map( - "3.0" -> "Some: Some" - ) + |""".stripMargin ) check( @@ -861,10 +868,7 @@ class CompletionSuite extends BaseCompletionSuite { |} |""".stripMargin, """|Some scala - |""".stripMargin, - compat = Map( - "3.0" -> "Some: Some" - ) + |""".stripMargin ) check( @@ -880,8 +884,8 @@ class CompletionSuite extends BaseCompletionSuite { topLines = Some(2), compat = Map( "3.0" -> - """|NoManifest=> reflect.NoManifest.type - |NoClassDefFoundError: NoClassDefFoundError + """|NoManifest: reflect.NoManifest.type + |NoClassDefFoundError java.lang |""".stripMargin ) ) @@ -905,9 +909,9 @@ class CompletionSuite extends BaseCompletionSuite { |Set scala.collection.immutable |""".stripMargin, "3.0" -> - """|Seq=> collection.immutable.Seq.type + """|Seq: collection.immutable.Seq.type |SeqCharSequence(sequenceOfChars: scala.collection.IndexedSeq[Char]): SeqCharSequence - |Set=> scala.collection.immutable.Set.type + |Set: scala.collection.immutable.Set.type |""".stripMargin ) ) @@ -931,9 +935,9 @@ class CompletionSuite extends BaseCompletionSuite { |Set scala.collection.immutable |""".stripMargin, "3.0" -> - """|SafeVarargs: java.lang.SafeVarargs - |SafeVarargs: SafeVarargs - |ScalaReflectionException: scala.ScalaReflectionException + """|SafeVarargs java.lang + |SafeVarargs java.lang + |ScalaReflectionException scala |""".stripMargin ) ) @@ -961,12 +965,12 @@ class CompletionSuite extends BaseCompletionSuite { "3.0.0-RC1" -> """|NotString: Int |Number: scala.util.matching.Regex - |Nil=> collection.immutable.Nil.type + |Nil: collection.immutable.Nil.type |""".stripMargin, "3.0" -> """|NotString: Int - |Nil=> collection.immutable.Nil.type - |NoManifest=> reflect.NoManifest.type + |Nil: collection.immutable.Nil.type + |NoManifest: reflect.NoManifest.type |""".stripMargin ) ) @@ -986,9 +990,9 @@ class CompletionSuite extends BaseCompletionSuite { topLines = Option(3), compat = Map( "3.0" -> - """|NegativeArraySizeException: java.lang.NegativeArraySizeException - |NegativeArraySizeException: NegativeArraySizeException - |Nil=> collection.immutable.Nil.type + """|NegativeArraySizeException java.lang + |NegativeArraySizeException java.lang + |Nil: collection.immutable.Nil.type |""".stripMargin ) ) @@ -1008,9 +1012,9 @@ class CompletionSuite extends BaseCompletionSuite { topLines = Option(3), compat = Map( "3.0" -> - """|NegativeArraySizeException: java.lang.NegativeArraySizeException - |NegativeArraySizeException: NegativeArraySizeException - |Nil=> collection.immutable.Nil.type + """|NegativeArraySizeException java.lang + |NegativeArraySizeException java.lang + |Nil: collection.immutable.Nil.type |""".stripMargin ) ) @@ -1072,15 +1076,7 @@ class CompletionSuite extends BaseCompletionSuite { |printnnn: String |print(x: Any): Unit |""".stripMargin, - topLines = Some(4), - compat = Map( - "3.0" -> - """|printxxx: String - |printmmm=> String - |printnnn=> String - |print(x: Any): Unit - |""".stripMargin - ) + topLines = Some(4) ) check( @@ -1114,8 +1110,36 @@ class CompletionSuite extends BaseCompletionSuite { |isInstanceOf[T0]: Boolean |""".stripMargin, compat = Map( - "3.0" -> "intNumber=> Int" + "3.0" -> + """|intNumber: Int + |""".stripMargin ) ) + check( + "using".tag(IgnoreScala2), + s"""|class Foo { + | def max[T](x: T, y: T)(using ord: Ordered[T]): T = + | if ord.compare(x, y) < 0 then y else x + |} + |object Main { + | new Foo().max@@ + |} + |""".stripMargin, + """|max[T](x: T, y: T)(using ord: Ordered[T]): T + |""".stripMargin + ) + + check( + "annon-context".tag(IgnoreScala2), + s"""|class Foo { + | def max[T](x: T, y: T)(using Ordered[T]): T = ??? + |} + |object Main { + | new Foo().max@@ + |} + |""".stripMargin, + """|max[T](x: T, y: T)(using Ordered[T]): T + |""".stripMargin + ) }