From 582eb46d8d132aac92a48070c9d9313c46e72b03 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Tue, 7 May 2024 01:22:07 +0200 Subject: [PATCH] chore: Backport changes for presentation compiler (#20345) Changes from: - https://github.com/scalameta/metals/pull/6225 - https://github.com/scalameta/metals/pull/6236 - https://github.com/scalameta/metals/pull/6241 - https://github.com/scalameta/metals/pull/6303 - https://github.com/scalameta/metals/pull/6308 [Cherry-picked 1cdf99f5cf77fc4304bb2cf3901a18e7becb90c2] --- .../main/dotty/tools/pc/HoverProvider.scala | 19 +- .../dotty/tools/pc/PcInlayHintsProvider.scala | 160 +++++++++------- .../tools/pc/ScalaPresentationCompiler.scala | 2 +- .../pc/completions/MatchCaseCompletions.scala | 3 +- .../pc/utils/InteractiveEnrichments.scala | 4 +- .../tools/pc/base/BaseInlayHintsSuite.scala | 6 +- .../completion/CompletionCaseSuite.scala | 37 +++- .../pc/tests/hover/HoverPlainTextSuite.scala | 90 +++++++++ .../pc/tests/inlayHints/InlayHintsSuite.scala | 172 +++++++++++++++++- .../tools/pc/utils/MockSymbolSearch.scala | 7 + project/Build.scala | 2 +- 11 files changed, 409 insertions(+), 93 deletions(-) create mode 100644 presentation-compiler/test/dotty/tools/pc/tests/hover/HoverPlainTextSuite.scala diff --git a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala index ba316b08619e..a667b038c894 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala @@ -5,6 +5,7 @@ import java.util as ju import scala.meta.internal.metals.Report import scala.meta.internal.metals.ReportContext import scala.meta.internal.pc.ScalaHover +import scala.meta.pc.ContentType import scala.meta.pc.HoverSignature import scala.meta.pc.OffsetParams import scala.meta.pc.SymbolSearch @@ -30,7 +31,8 @@ object HoverProvider: def hover( params: OffsetParams, driver: InteractiveDriver, - search: SymbolSearch + search: SymbolSearch, + contentType: ContentType )(implicit reportContext: ReportContext): ju.Optional[HoverSignature] = val uri = params.uri().nn val text = params.text().nn @@ -101,10 +103,10 @@ object HoverProvider: skipCheckOnName ) match case Nil => - fallbackToDynamics(path, printer) + fallbackToDynamics(path, printer, contentType) case (symbol, tpe) :: _ if symbol.name == nme.selectDynamic || symbol.name == nme.applyDynamic => - fallbackToDynamics(path, printer) + fallbackToDynamics(path, printer, contentType) case symbolTpes @ ((symbol, tpe) :: _) => val exprTpw = tpe.widenTermRefExpr.deepDealias val hoverString = @@ -126,7 +128,7 @@ object HoverProvider: end hoverString val docString = symbolTpes - .flatMap(symTpe => search.symbolDocumentation(symTpe._1)) + .flatMap(symTpe => search.symbolDocumentation(symTpe._1, contentType)) .map(_.docstring()) .mkString("\n") printer.expressionType(exprTpw) match @@ -144,7 +146,8 @@ object HoverProvider: symbolSignature = Some(hoverString), docstring = Some(docString), forceExpressionType = forceExpressionType, - contextInfo = printer.getUsedRenamesInfo + contextInfo = printer.getUsedRenamesInfo, + contentType = contentType ) ).nn case _ => @@ -159,7 +162,8 @@ object HoverProvider: private def fallbackToDynamics( path: List[Tree], - printer: ShortenedTypePrinter + printer: ShortenedTypePrinter, + contentType: ContentType )(using Context): ju.Optional[HoverSignature] = path match case SelectDynamicExtractor(sel, n, name) => def findRefinement(tp: Type): Option[HoverSignature] = @@ -178,7 +182,8 @@ object HoverProvider: new ScalaHover( expressionType = Some(tpeString), symbolSignature = Some(s"$valOrDef $name$tpeString"), - contextInfo = printer.getUsedRenamesInfo + contextInfo = printer.getUsedRenamesInfo, + contentType = contentType ) ) case RefinedType(parent, _, _) => diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index 70aaa82eae05..c4fdb97c0418 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -42,6 +42,7 @@ class PcInlayHintsProvider( val source = SourceFile.virtual(filePath.toString, sourceText) driver.run(uri, source) + given InlayHintsParams = params given InferredType.Text = InferredType.Text(text) given ctx: Context = driver.currentCtx @@ -65,7 +66,7 @@ class PcInlayHintsProvider( tree: Tree, ): InlayHints = tree match - case ImplicitConversion(symbol, range) if params.implicitConversions() => + case ImplicitConversion(symbol, range) => val adjusted = adjustPos(range) inlayHints .add( @@ -78,8 +79,7 @@ class PcInlayHintsProvider( LabelPart(")") :: Nil, InlayHintKind.Parameter, ) - case ImplicitParameters(symbols, pos, allImplicit) - if params.implicitParameters() => + case ImplicitParameters(symbols, pos, allImplicit) => val labelParts = symbols.map(s => List(labelPart(s, s.decodedName))) val label = if allImplicit then labelParts.separated("(using ", ", ", ")") @@ -89,14 +89,14 @@ class PcInlayHintsProvider( label, InlayHintKind.Parameter, ) - case ValueOf(label, pos) if params.implicitParameters() => + case ValueOf(label, pos) => inlayHints.add( adjustPos(pos).toLsp, LabelPart("(") :: LabelPart(label) :: List(LabelPart(")")), InlayHintKind.Parameter, ) case TypeParameters(tpes, pos, sel) - if params.typeParameters() && !syntheticTupleApply(sel) => + if !syntheticTupleApply(sel) => val label = tpes.map(toLabelParts(_, pos)).separated("[", ", ", "]") inlayHints.add( adjustPos(pos).endPos.toLsp, @@ -104,7 +104,7 @@ class PcInlayHintsProvider( InlayHintKind.Type, ) case InferredType(tpe, pos, defTree) - if params.inferredTypes() && !isErrorTpe(tpe) => + if !isErrorTpe(tpe) => val adjustedPos = adjustPos(pos).endPos if inlayHints.containsDef(adjustedPos.start) then inlayHints else @@ -191,14 +191,16 @@ class PcInlayHintsProvider( end PcInlayHintsProvider object ImplicitConversion: - def unapply(tree: Tree)(using Context) = - tree match - case Apply(fun: Ident, args) if isSynthetic(fun) => - implicitConversion(fun, args) - case Apply(Select(fun, name), args) - if name == nme.apply && isSynthetic(fun) => - implicitConversion(fun, args) - case _ => None + def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) = + if (params.implicitConversions()) { + tree match + case Apply(fun: Ident, args) if isSynthetic(fun) => + implicitConversion(fun, args) + case Apply(Select(fun, name), args) + if name == nme.apply && isSynthetic(fun) => + implicitConversion(fun, args) + case _ => None + } else None private def isSynthetic(tree: Tree)(using Context) = tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) @@ -212,52 +214,64 @@ object ImplicitConversion: end ImplicitConversion object ImplicitParameters: - def unapply(tree: Tree)(using Context) = - tree match - case Apply(fun, args) - if args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent => - val (implicitArgs, providedArgs) = args.partition(isSyntheticArg) - val allImplicit = providedArgs.isEmpty || providedArgs.forall { - case Ident(name) => name == nme.MISSING - case _ => false - } - val pos = implicitArgs.head.sourcePos - Some(implicitArgs.map(_.symbol), pos, allImplicit) - case _ => None + def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) = + if (params.implicitParameters()) { + tree match + case Apply(fun, args) + if args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent => + val (implicitArgs, providedArgs) = args.partition(isSyntheticArg) + val allImplicit = providedArgs.isEmpty || providedArgs.forall { + case Ident(name) => name == nme.MISSING + case _ => false + } + val pos = implicitArgs.head.sourcePos + Some(implicitArgs.map(_.symbol), pos, allImplicit) + case _ => None + } else None private def isSyntheticArg(tree: Tree)(using Context) = tree match case tree: Ident => - tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) + tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) && + !isQuotes(tree) case _ => false + + // Decorations for Quotes are rarely useful + private def isQuotes(tree: Tree)(using Context) = + tree.tpe.typeSymbol == defn.QuotesClass + end ImplicitParameters object ValueOf: - def unapply(tree: Tree)(using Context) = - tree match - case Apply(ta @ TypeApply(fun, _), _) - if fun.span.isSynthetic && isValueOf(fun) => - Some( - "new " + tpnme.valueOf.decoded.capitalize + "(...)", - fun.sourcePos, - ) - case _ => None + def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) = + if (params.implicitParameters()) { + tree match + case Apply(ta @ TypeApply(fun, _), _) + if fun.span.isSynthetic && isValueOf(fun) => + Some( + "new " + tpnme.valueOf.decoded.capitalize + "(...)", + fun.sourcePos, + ) + case _ => None + } else None private def isValueOf(tree: Tree)(using Context) = val symbol = tree.symbol.maybeOwner symbol.name.decoded == tpnme.valueOf.decoded.capitalize end ValueOf object TypeParameters: - def unapply(tree: Tree)(using Context) = - tree match - case TypeApply(sel: Select, _) if sel.isForComprehensionMethod => None - case TypeApply(fun, args) if inferredTypeArgs(args) => - val pos = fun match - case sel: Select if sel.isInfix => - sel.sourcePos.withEnd(sel.nameSpan.end) - case _ => fun.sourcePos - val tpes = args.map(_.typeOpt.stripTypeVar.widen.finalResultType) - Some((tpes, pos.endPos, fun)) - case _ => None + def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) = + if (params.typeParameters()) { + tree match + case TypeApply(sel: Select, _) + if sel.isForComprehensionMethod || sel.isInfix || + sel.symbol.name == nme.unapply => + None + case TypeApply(fun, args) if inferredTypeArgs(args) => + val tpes = args.map(_.tpe.stripTypeVar.widen.finalResultType) + Some((tpes, fun.sourcePos.endPos, fun)) + case _ => None + } else None + private def inferredTypeArgs(args: List[Tree]): Boolean = args.forall { case tt: TypeTree if tt.span.exists && !tt.span.isZeroExtent => true @@ -270,29 +284,35 @@ object InferredType: object Text: def apply(text: Array[Char]): Text = text - def unapply(tree: Tree)(using text: Text, cxt: Context) = - tree match - case vd @ ValDef(_, tpe, _) - if isValidSpan(tpe.span, vd.nameSpan) && - !vd.symbol.is(Flags.Enum) && - !isValDefBind(text, vd) => - if vd.symbol == vd.symbol.sourceSymbol then - Some(tpe.typeOpt, tpe.sourcePos.withSpan(vd.nameSpan), vd) - else None - case vd @ DefDef(_, _, tpe, _) - if isValidSpan(tpe.span, vd.nameSpan) && - tpe.span.start >= vd.nameSpan.end && - !vd.symbol.isConstructor && - !vd.symbol.is(Flags.Mutable) => - if vd.symbol == vd.symbol.sourceSymbol then - Some(tpe.typeOpt, tpe.sourcePos, vd) - else None - case bd @ Bind( - name, - Ident(nme.WILDCARD), - ) => - Some(bd.symbol.info, bd.namePos, bd) - case _ => None + def unapply(tree: Tree)(using params: InlayHintsParams, text: Text, ctx: Context) = + if (params.inferredTypes()) { + tree match + case vd @ ValDef(_, tpe, _) + if isValidSpan(tpe.span, vd.nameSpan) && + !vd.symbol.is(Flags.Enum) && + (isNotInUnapply(vd) || params.hintsInPatternMatch()) && + !isValDefBind(text, vd) => + if vd.symbol == vd.symbol.sourceSymbol then + Some(tpe.tpe, tpe.sourcePos.withSpan(vd.nameSpan), vd) + else None + case vd @ DefDef(_, _, tpe, _) + if isValidSpan(tpe.span, vd.nameSpan) && + tpe.span.start >= vd.nameSpan.end && + !vd.symbol.isConstructor && + !vd.symbol.is(Flags.Mutable) => + if vd.symbol == vd.symbol.sourceSymbol then + Some(tpe.tpe, tpe.sourcePos, vd) + else None + case bd @ Bind( + name, + Ident(nme.WILDCARD), + ) if !bd.span.isZeroExtent && bd.symbol.isTerm && params.hintsInPatternMatch() => + Some(bd.symbol.info, bd.namePos, bd) + case _ => None + } else None + + private def isNotInUnapply(vd: ValDef)(using Context) = + vd.rhs.span.exists && vd.rhs.span.start > vd.nameSpan.end private def isValidSpan(tpeSpan: Span, nameSpan: Span): Boolean = tpeSpan.isZeroExtent && diff --git a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala index 378564d90bc1..86aa895cb4fc 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala @@ -360,7 +360,7 @@ case class ScalaPresentationCompiler( params.token() ) { access => val driver = access.compiler() - HoverProvider.hover(params, driver, search) + HoverProvider.hover(params, driver, search, config.hoverContentType()) } end hover diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala index d8f35f893827..dcc2d0ea705a 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala @@ -23,6 +23,7 @@ import dotty.tools.dotc.core.Symbols.NoSymbol import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.Types.AndType import dotty.tools.dotc.core.Types.ClassInfo +import dotty.tools.dotc.core.Types.NoType import dotty.tools.dotc.core.Types.OrType import dotty.tools.dotc.core.Types.Type import dotty.tools.dotc.core.Types.TypeRef @@ -94,7 +95,7 @@ object CaseKeywordCompletion: Some(sel.tpe.widen.deepDealias) selTpe - .map { selTpe => + .collect { case selTpe if selTpe != NoType => val selectorSym = selTpe.typeSymbol // Special handle case when selector is a tuple or `FunctionN`. if definitions.isTupleClass(selectorSym) || definitions.isFunctionClass( diff --git a/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala b/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala index fb41e8859801..dd2fb3107c49 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala @@ -4,6 +4,7 @@ import scala.annotation.tailrec import scala.meta.internal.jdk.CollectionConverters.* import scala.meta.internal.mtags.CommonMtagsEnrichments import scala.meta.internal.mtags.KeywordWrapper +import scala.meta.pc.ContentType import scala.meta.pc.OffsetParams import scala.meta.pc.RangeParams import scala.meta.pc.SymbolDocumentation @@ -260,7 +261,7 @@ object InteractiveEnrichments extends CommonMtagsEnrichments: } extension (search: SymbolSearch) - def symbolDocumentation(symbol: Symbol)(using + def symbolDocumentation(symbol: Symbol, contentType: ContentType = ContentType.MARKDOWN)(using Context ): Option[SymbolDocumentation] = def toSemanticdbSymbol(symbol: Symbol) = @@ -280,6 +281,7 @@ object InteractiveEnrichments extends CommonMtagsEnrichments: val documentation = search.documentation( sym, () => parentSymbols.iterator.map(toSemanticdbSymbol).toList.asJava, + contentType, ) documentation.nn.toScala end symbolDocumentation diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala index 94b00ca82aea..78635e540c43 100644 --- a/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala @@ -18,6 +18,7 @@ class BaseInlayHintsSuite extends BasePCSuite { base: String, expected: String, kind: Option[Int] = None, + hintsInPatternMatch: Boolean = false ): Unit = def pkgWrap(text: String) = if (text.contains("package")) text @@ -35,7 +36,8 @@ class BaseInlayHintsSuite extends BasePCSuite { true, true, true, - true + true, + hintsInPatternMatch ) val inlayHints = presentationCompiler @@ -49,8 +51,8 @@ class BaseInlayHintsSuite extends BasePCSuite { val obtained = TestInlayHints.applyInlayHints(withPkg, inlayHints) assertNoDiff( + pkgWrap(expected), obtained, - pkgWrap(expected) ) } \ No newline at end of file diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala index 521880b3a84b..e72ee5221d91 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionCaseSuite.scala @@ -763,14 +763,14 @@ class CompletionCaseSuite extends BaseCompletionSuite: | |object O { | val x: Foo | Bar = ??? - | val y = List(x).map{ ca@@ } + | val y = List(x).map{ca@@ } |}""".stripMargin, s"""|case class Foo(a: Int) |case class Bar(b: Int) | |object O { | val x: Foo | Bar = ??? - | val y = List(x).map{ + | val y = List(x).map{ |\tcase Foo(a) => $$0 |\tcase Bar(b) => | } @@ -779,3 +779,36 @@ class CompletionCaseSuite extends BaseCompletionSuite: filter = _.contains("exhaustive") ) + @Test def summonFrom = + check( + """ + |object A { + | import scala.compiletime.summonFrom + | class A + | + | inline def f: Any = summonFrom { + | case x@@: A => ??? // error: ambiguous givens + | } + |} + |""".stripMargin, + "" + ) + + @Test def summonFrom2 = + check( + """ + |object A { + | import scala.compiletime.summonFrom + | + | class A + | given a1: A = new A + | given a2: A = new A + | + | inline def f: Any = summonFrom { + | case x@@: A => ??? // error: ambiguous givens + | } + |} + |""".stripMargin, + "" + ) + diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverPlainTextSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverPlainTextSuite.scala new file mode 100644 index 000000000000..a69a1ff0f5da --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverPlainTextSuite.scala @@ -0,0 +1,90 @@ +package dotty.tools.pc.tests.hover + +import dotty.tools.pc.base.BaseHoverSuite + +import org.junit.Test +import dotty.tools.pc.utils.MockEntries +import scala.meta.pc.SymbolDocumentation +import scala.meta.internal.pc.PresentationCompilerConfigImpl +import scala.meta.pc.ContentType +import scala.meta.pc.PresentationCompilerConfig + +class HoverPlainTextSuite extends BaseHoverSuite: + + override protected def config: PresentationCompilerConfig = + PresentationCompilerConfigImpl().copy( + snippetAutoIndent = false, + hoverContentType = ContentType.PLAINTEXT + ) + + override protected def mockEntries: MockEntries = new MockEntries: + override def documentations: Set[SymbolDocumentation] = Set( + ScalaMockDocumentation("java/lang/String#substring().", "substring", List(), List(MockParam("beginIndex"))), + ScalaMockDocumentation("java/util/Collections#emptyList().", "emptyList"), + ScalaMockDocumentation("_empty_/Alpha.apply().", "apply", List(), List(MockParam("x"))), + ScalaMockDocumentation("_empty_/Alpha#", "init", List(), List(MockParam("x"))), + ScalaMockDocumentation("scala/collection/LinearSeqOps#headOption().", "headOption"), + ScalaMockDocumentation("scala/Option#fold().", "fold", List(MockParam("B"))), + ) + + @Test def `basic-plaintext` = + check( + """| + |/** + | * Some docstring + | */ + |case class Alpha(x: Int) { + |} + | + |object Main { + | val x = <> + |} + |""".stripMargin, + """|def apply(x: Int): Alpha + | + |Found documentation for _empty_/Alpha.apply(). + | + |""".stripMargin + ) + + + @Test def `head-plaintext` = + check( + """|object a { + | <> + |} + |""".stripMargin, + """|override def headOption: Option[Int] + | + |Found documentation for scala/collection/LinearSeqOps#headOption(). + |""".stripMargin + ) + + @Test def `trait-plaintext` = + check( + """|trait XX + |object Main extends <>{} + |""".stripMargin, + "trait XX: XX", + ) + + @Test def `function-chain4-plaintext` = + check( + """ + |trait Consumer { + | def subConsumer[T](i: T): T + | def consume(value: Int)(n: Int): Unit + |} + | + |object O { + | val consumer: Consumer = ??? + | List(1).foreach(<>.consume(1)) + |} + |""".stripMargin, + """|Expression type: + |Consumer + | + |Symbol signature: + |def subConsumer[T](i: T): T + |""".stripMargin + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala index da7601e3c746..e470f492657c 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala @@ -170,7 +170,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |} |""".stripMargin, """|object O { - | def m/*: List<>[Int<>]*/ = 1 ::/*[Int<>]*/ List/*[Int<>]*/(1) + | def m/*: List<>[Int<>]*/ = 1 :: List/*[Int<>]*/(1) |} |""".stripMargin ) @@ -418,13 +418,16 @@ class InlayHintsSuite extends BaseInlayHintsSuite { @Test def `tuple-unapply` = check( """|object Main { + | val (local, _) = ("", 1.0) | val (fst, snd) = (1, 2) |} |""".stripMargin, """|object Main { + | val (local/*: String<>*/, _) = ("", 1.0) | val (fst/*: Int<>*/, snd/*: Int<>*/) = (1, 2) |} - |""".stripMargin + |""".stripMargin, + hintsInPatternMatch = true ) @Test def `list-unapply` = @@ -434,7 +437,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |} |""".stripMargin, """|object Main { - | val hd/*: Int<>*/ ::/*[Int<>]*/ tail/*: List<>[Int<>]*/ = List/*[Int<>]*/(1, 2) + | val hd :: tail = List/*[Int<>]*/(1, 2) |} |""".stripMargin, ) @@ -449,7 +452,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |""".stripMargin, """|object Main { | val x/*: Int<>*/ = List/*[Int<>]*/(1, 2) match { - | case hd/*: Int<>*/ ::/*[Int<>]*/ tail/*: List<>[Int<>]*/ => hd + | case hd :: tail => hd | } |} |""".stripMargin, @@ -464,9 +467,10 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |""".stripMargin, """|object Main { |case class Foo[A](x: A, y: A) - | val Foo/*[Int<>]*/(fst/*: Int<>*/, snd/*: Int<>*/) = Foo/*[Int<>]*/(1, 2) + | val Foo(fst/*: Int<>*/, snd/*: Int<>*/) = Foo/*[Int<>]*/(1, 2) |} |""".stripMargin, + hintsInPatternMatch = true ) @Test def `valueOf` = @@ -517,7 +521,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |} |""".stripMargin, """|object Main { - | List/*[Int<>]*/(1).collect/*[Int<>]*/ { case x/*: Int<>*/ => x } + | List/*[Int<>]*/(1).collect/*[Int<>]*/ { case x => x } | val x: PartialFunction[Int, Int] = { | case 1 => 2 | } @@ -532,7 +536,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |} |""".stripMargin, """|object O { - | val tupleBound @ (one/*: String<>*/, two/*: String<>*/) = ("1", "2") + | val tupleBound @ (one, two) = ("1", "2") |} |""".stripMargin ) @@ -546,7 +550,8 @@ class InlayHintsSuite extends BaseInlayHintsSuite { """|object O { | val tupleBound /* comment */ @ (one/*: String<>*/, two/*: String<>*/) = ("1", "2") |} - |""".stripMargin + |""".stripMargin, + hintsInPatternMatch = true ) @Test def `complex` = @@ -764,4 +769,155 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |} |""".stripMargin ) + + @Test def `pattern-match` = + check( + """|package example + |object O { + | val head :: tail = List(1) + | List(1) match { + | case head :: next => + | case Nil => + | } + | Option(Option(1)) match { + | case Some(Some(value)) => + | case None => + | } + | val (local, _) = ("", 1.0) + | val Some(x) = Option(1) + | for { + | x <- List((1,2)) + | (z, y) = x + | } yield { + | x + | } + |} + |""".stripMargin, + """|package example + |object O { + | val head :: tail = List/*[Int<>]*/(1) + | List/*[Int<>]*/(1) match { + | case head :: next => + | case Nil => + | } + | Option/*[Option<>[Int<>]]*/(Option/*[Int<>]*/(1)) match { + | case Some(Some(value)) => + | case None => + | } + | val (local, _) = ("", 1.0) + | val Some(x) = Option/*[Int<>]*/(1) + | for { + | x <- List/*[(Int<>, Int<>)]*/((1,2)) + | (z, y) = x + | } yield { + | x + | } + |} + |""".stripMargin + ) + + + @Test def `pattern-match1` = + check( + """|package example + |object O { + | val head :: tail = List(1) + | List(1) match { + | case head :: next => + | case Nil => + | } + | Option(Option(1)) match { + | case Some(Some(value)) => + | case None => + | } + | val (local, _) = ("", 1.0) + | val Some(x) = Option(1) + | for { + | x <- List((1,2)) + | (z, y) = x + | } yield { + | x + | } + |} + |""".stripMargin, + """|package example + |object O { + | val head/*: Int<>*/ :: tail/*: List<>[Int<>]*/ = List/*[Int<>]*/(1) + | List/*[Int<>]*/(1) match { + | case head/*: Int<>*/ :: next/*: List<>[Int<>]*/ => + | case Nil => + | } + | Option/*[Option<>[Int<>]]*/(Option/*[Int<>]*/(1)) match { + | case Some(Some(value/*: Int<>*/)) => + | case None => + | } + | val (local/*: String<>*/, _) = ("", 1.0) + | val Some(x/*: Int<>*/) = Option/*[Int<>]*/(1) + | for { + | x/*: (Int<>, Int<>)*/ <- List/*[(Int<>, Int<>)]*/((1,2)) + | (z/*: Int<>*/, y/*: Int<>*/) = x + | } yield { + | x + | } + |} + |""".stripMargin, + hintsInPatternMatch = true + ) + + @Test def quotes = + check( + """|package example + |import scala.quoted.* + |object O: + | inline def foo[T]: List[String] = ${fooImpl[T]} + | def fooImpl[T: Type](using Quotes): Expr[List[String]] = ??? + |""".stripMargin, + """|package example + |import scala.quoted.* + |object O: + | inline def foo[T]: List[String] = ${fooImpl[T]} + | def fooImpl[T: Type](using Quotes): Expr[List[String]] = ??? + |""".stripMargin + ) + + @Test def quotes1 = + check( + """|package example + |import scala.quoted.* + |object O: + | def matchTypeImpl[T: Type](param1: Expr[T])(using Quotes) = + | import quotes.reflect.* + | Type.of[T] match + | case '[f] => + | val fr = TypeRepr.of[T] + |""".stripMargin, + """|package example + |import scala.quoted.* + |object O: + | def matchTypeImpl[T: Type](param1: Expr[T])(using Quotes)/*: Unit<>*/ = + | import quotes.reflect.* + | Type.of[T] match + | case '[f] => + | val fr/*: TypeRepr<>*/ = TypeRepr.of[T]/*(using evidence$1<<(3:21)>>)*/ + |""".stripMargin + ) + + + @Test def quotes2 = + check( + """|package example + |import scala.quoted.* + |object O: + | def rec[A : Type](using Quotes): List[String] = + | Type.of[A] match + | case '[field *: fields] => ??? + |""".stripMargin, + """|package example + |import scala.quoted.* + |object O: + | def rec[A : Type](using Quotes): List[String] = + | Type.of[A] match + | case '[field *: fields] => ??? + |""".stripMargin + ) } diff --git a/presentation-compiler/test/dotty/tools/pc/utils/MockSymbolSearch.scala b/presentation-compiler/test/dotty/tools/pc/utils/MockSymbolSearch.scala index edd339a5e2ed..9015a39ba9e7 100644 --- a/presentation-compiler/test/dotty/tools/pc/utils/MockSymbolSearch.scala +++ b/presentation-compiler/test/dotty/tools/pc/utils/MockSymbolSearch.scala @@ -7,6 +7,7 @@ import java.util as ju import scala.jdk.CollectionConverters.* import scala.jdk.OptionConverters.* import scala.meta.internal.metals.{ClasspathSearch, WorkspaceSymbolQuery} +import scala.meta.pc.ContentType import scala.meta.pc.SymbolSearch.Result import scala.meta.pc.{ ParentSymbols, @@ -66,6 +67,12 @@ class MockSymbolSearch( override def documentation( symbol: String, parents: ParentSymbols + ) = documentation(symbol, parents, ContentType.MARKDOWN) + + override def documentation( + symbol: String, + parents: ParentSymbols, + contentType: ContentType ): Optional[SymbolDocumentation] = (symbol +: parents.parents().asScala).iterator .map(symbol => mockEntries.documentations.find(_.symbol == symbol)) diff --git a/project/Build.scala b/project/Build.scala index 3730e5b096d4..f65fbfbd89f0 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1131,7 +1131,7 @@ object Build { BuildInfoPlugin.buildInfoDefaultSettings lazy val presentationCompilerSettings = { - val mtagsVersion = "1.2.2+44-42e0515a-SNAPSHOT" + val mtagsVersion = "1.3.0+56-a06a024d-SNAPSHOT" Seq( resolvers ++= Resolver.sonatypeOssRepos("snapshots"),