Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 89 additions & 52 deletions presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import scala.meta.pc.SymbolSearch
import scala.meta.pc.reports.ReportContext

import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.ast.untpd.InferredTypeTree
import dotty.tools.dotc.ast.untpd
import dotty.tools.dotc.core.Constants.*
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Decorators.*
Expand Down Expand Up @@ -60,8 +60,79 @@ object HoverProvider:
// For expression we need to find all enclosing applies to get the exact generic type
val enclosing = path.expandRangeToEnclosingApply(pos)

lazy val printer = ShortenedTypePrinter(search, IncludeDefaultParam.Include)(
using indexedContext
)

def hoverSignature(
symbol: Symbol,
symbolTpes: Seq[(Symbol, Type, Option[String])],
tpe: Type
): ju.Optional[HoverSignature] = {
val exprTpw = tpe.widenTermRefExpr.deepDealiasAndSimplify
val hoverString =
tpw match
// https://github.com/scala/scala3/issues/8891
case tpw: ImportType =>
printer.hoverSymbol(symbol, symbol.paramRef)
case _ =>
val (innerTpe, sym) =
if symbol.isType then (symbol.typeRef, symbol)
else enclosing.head.seenFrom(symbol)

val finalTpe =
if innerTpe != NoType then innerTpe
else tpw

printer.hoverSymbol(sym, finalTpe.deepDealiasAndSimplify)
end match
end hoverString

val docString = symbolTpes
.flatMap(symTpe => search.symbolDocumentation(symTpe._1, contentType))
.map(_.docstring())
.mkString("\n")

val expresionTypeOpt =
if symbol.name == StdNames.nme.??? then
InferExpectedType(search, driver, params).infer()
else printer.expressionType(exprTpw)
expresionTypeOpt match
case Some(expressionType) =>
val forceExpressionType =
!pos.span.isZeroExtent || (
!hoverString.endsWith(expressionType) &&
!symbol.isType &&
!symbol.is(Module) &&
!symbol.flags.isAllOf(EnumCase)
)
ju.Optional.of(
new ScalaHover(
expressionType = Some(expressionType),
symbolSignature = Some(hoverString),
docstring = Some(docString),
forceExpressionType = forceExpressionType,
contextInfo = printer.getUsedRenamesInfo,
contentType = contentType
)
).nn
case _ =>
ju.Optional.empty().nn
}

if tp.isError || tpw == NoType || tpw.isError || path.isEmpty
then
val untpdPath = Interactive.resolveTypedOrUntypedPath(enclosing, pos)(using ctx)
val derivesClauseSymbolOpt = untpdPath match
/* In case of `class X derives TC@@` we shouldn't add `[]`
*/
case Ident(_) :: (templ: untpd.DerivingTemplate) :: _ =>
templ.derived.find(_.sourcePos.contains(pos)).collect {
case ident if ident.tpe != null => ident.symbol -> ident.tpe.nn
}
case _ =>
None

def report =
val posId =
if path.isEmpty || !path.head.sourcePos.exists
Expand All @@ -88,16 +159,16 @@ object HoverProvider:
|""".stripMargin,
s"$uri::$posId"
)
reportContext.unsanitized.nn.create(() => report, /*ifVerbose =*/ true)
ju.Optional.empty().nn

derivesClauseSymbolOpt match
case Some((symbol, tpe)) =>
hoverSignature(symbol, List((symbol, tpe, None)), tpe)
case None =>
reportContext.unsanitized.nn.create(() => report, /*ifVerbose =*/ true)
ju.Optional.empty().nn
else
val skipCheckOnName =
!pos.isPoint // don't check isHoveringOnName for RangeHover

val printerCtx = Interactive.contextOfPath(path)
val printer = ShortenedTypePrinter(search, IncludeDefaultParam.Include)(
using IndexedContext(pos)(using printerCtx)
)
MetalsInteractive.enclosingSymbolsWithExpressionType(
enclosing,
pos,
Expand All @@ -109,57 +180,23 @@ object HoverProvider:
case (symbol, tpe, _) :: _
if symbol.name == nme.selectDynamic || symbol.name == nme.applyDynamic =>
fallbackToDynamics(path, printer, contentType)
case symbolTpes @ ((symbol, tpe, _) :: _) =>
case symbolTpes @ ((symbol, tpe, None) :: _) =>
hoverSignature(symbol, symbolTpes, tpe)
case (_, tpe, Some(namedTupleArg)) :: _ =>
val exprTpw = tpe.widenTermRefExpr.deepDealiasAndSimplify
val hoverString =
tpw match
// https://github.com/lampepfl/dotty/issues/8891
case tpw: ImportType =>
printer.hoverSymbol(symbol, symbol.paramRef)
case _ =>
val (tpe, sym) =
if symbol.isType then (symbol.typeRef, symbol)
else enclosing.head.seenFrom(symbol)

val finalTpe =
if tpe != NoType then tpe
else tpw

printer.hoverSymbol(sym, finalTpe.deepDealiasAndSimplify)
end match
end hoverString

val docString = symbolTpes
.flatMap(symTpe => search.symbolDocumentation(symTpe._1, contentType))
.map(_.docstring())
.mkString("\n")

val expresionTypeOpt =
if symbol.name == nme.??? then
InferExpectedType(search, driver, params).infer()
else printer.expressionType(exprTpw)
expresionTypeOpt match
case Some(expressionType) =>
val forceExpressionType =
!pos.span.isZeroExtent || (
!hoverString.endsWith(expressionType) &&
!symbol.isType &&
!symbol.is(Module) &&
!symbol.flags.isAllOf(EnumCase)
)
printer.expressionType(exprTpw) match
case Some(tpe) =>
ju.Optional.of(
new ScalaHover(
expressionType = Some(expressionType),
symbolSignature = Some(hoverString),
docstring = Some(docString),
forceExpressionType = forceExpressionType,
expressionType = Some(tpe),
symbolSignature = Some(s"$namedTupleArg: $tpe"),
docstring = None,
forceExpressionType = false,
contextInfo = printer.getUsedRenamesInfo,
contentType = contentType
)
).nn
case _ =>
ju.Optional.empty().nn
end match
case _ => ju.Optional.empty().nn
end if
end hover

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,15 @@ class TypeDefinitionSuite extends BasePcDefinitionSuite:
|}
|""".stripMargin
)

@Test def `derives-typeclass-definition` =
check(
"""|package bar
|trait <<Foo>>[T]
|
|object Foo:
| def derived[T]: Foo[T] = ???
|
|case class Pet(name: String, kind: String) derives F@@oo
|""".stripMargin
)
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,17 @@ class HoverDefnSuite extends BaseHoverSuite:
|""".stripMargin,
"val `foo bar`: Int".hover
)

/** https://github.com/scala/scala3/issues/19489 */
@Test def `derives-typeclass` =
check(
"""|package bar
|trait Foo[T]
|
|object Foo:
| def derived[T]: Foo[T] = ???
|
|case class Pet(name: String, kind: String) derives F@@oo
|""".stripMargin,
"trait Foo: Foo".hover
)
Loading