diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 1d8c5d9b0c84..ead84dc9f7d0 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -10,6 +10,7 @@ import util.Spans.Span import core.Types.{ErrorType, MethodType, PolyType} import reporting._ +import dotty.tools.dotc.core.Types.Type object Signatures { @@ -48,31 +49,39 @@ object Signatures { */ def callInfo(path: List[tpd.Tree], span: Span)(using Context): (Int, Int, List[SingleDenotation]) = path match { + case UnApply(fun, _, patterns) :: _ => + callInfo(span, patterns, fun, Signatures.countParams(fun)) case Apply(fun, params) :: _ => - val alreadyAppliedCount = Signatures.countParams(fun) - val paramIndex = params.indexWhere(_.span.contains(span)) match { - case -1 => (params.length - 1 max 0) + alreadyAppliedCount - case n => n + alreadyAppliedCount - } - - val (alternativeIndex, alternatives) = fun.tpe match { - case err: ErrorType => - val (alternativeIndex, alternatives) = alternativesFromError(err, params) - (alternativeIndex, alternatives) + callInfo(span, params, fun, Signatures.countParams(fun)) + case _ => + (0, 0, Nil) + } - case _ => - val funSymbol = fun.symbol - val alternatives = funSymbol.owner.info.member(funSymbol.name).alternatives - val alternativeIndex = alternatives.map(_.symbol).indexOf(funSymbol) max 0 - (alternativeIndex, alternatives) - } + def callInfo( + span: Span, + params: List[Tree[Type]], + fun: Tree[Type], + alreadyAppliedCount : Int + )(using Context): (Int, Int, List[SingleDenotation]) = + val paramIndex = params.indexWhere(_.span.contains(span)) match { + case -1 => (params.length - 1 max 0) + alreadyAppliedCount + case n => n + alreadyAppliedCount + } - (paramIndex, alternativeIndex, alternatives) + val (alternativeIndex, alternatives) = fun.tpe match { + case err: ErrorType => + val (alternativeIndex, alternatives) = alternativesFromError(err, params) + (alternativeIndex, alternatives) case _ => - (0, 0, Nil) + val funSymbol = fun.symbol + val alternatives = funSymbol.owner.info.member(funSymbol.name).alternatives + val alternativeIndex = alternatives.map(_.symbol).indexOf(funSymbol) max 0 + (alternativeIndex, alternatives) } + (paramIndex, alternativeIndex, alternatives) + def toSignature(denot: SingleDenotation)(using Context): Option[Signature] = { val symbol = denot.symbol val docComment = ParsedComment.docOf(symbol) diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index 3ba0dfd047d5..c91d66877c6b 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -556,8 +556,7 @@ class DottyLanguageServer extends LanguageServer val pos = sourcePosition(driver, uri, params.getPosition) val trees = driver.openedTrees(uri) - val path = Interactive.pathTo(trees, pos).dropWhile(!_.isInstanceOf[Apply]) - + val path = Interactive.pathTo(trees, pos).dropWhile(t => !t.isInstanceOf[Apply | UnApply]) val (paramN, callableN, alternatives) = Signatures.callInfo(path, pos.span) val signatureInfos = alternatives.flatMap(Signatures.toSignature) diff --git a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala index e23100cdcf4f..2f76491ce2c6 100644 --- a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala +++ b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala @@ -361,4 +361,76 @@ class SignatureHelpTest { )), Some("Int"), Some("Buzzes a fizz up to bar")) ), None, 0) } + + @Test def unapplyMethod: Unit = { + code"""|object Main { + | Option(1) match { + | case Some(${m1}) => + | } + |}""" + .withSource + .signatureHelp(m1, List( + S("unapply[A]", Nil, List(List( + P("x$0", "Some[A]", None), + )), Some("Option[A]"), None) + ), None, 0) + } + + @Test def unapplyMethodImplicits: Unit = { + code"""| + |object Opt: + | def unapply[A](using String)(a: Option[A])(using Int) = a + | + |object Main { + | given String = "" + | given Int = 0 + | Option(1) match { + | case Opt(${m1}) => + | } + |}""" + .withSource + .signatureHelp(m1, List( + S("unapply[A]", Nil, List( + List( + P("x$1", "String", None, isImplicit = true) + ), + List( + P("a", "Option[A]", None), + ), + List( + P("x$3", "Int", None, isImplicit = true) + ) + ), + Some("Option[A]"), None) + ), None, 1) + } + + @Test def unapplyMethodImplicitsMultiple: Unit = { + code"""| + |object Opt: + | def unapply[A](using String)(using Int)(a: Option[A]) = a + | + |object Main { + | given String = "" + | given Int = 0 + | Option(1) match { + | case Opt(${m1}) => + | } + |}""" + .withSource + .signatureHelp(m1, List( + S("unapply[A]", Nil, List( + List( + P("x$1", "String", None, isImplicit = true) + ), + List( + P("x$2", "Int", None, isImplicit = true) + ), + List( + P("a", "Option[A]", None), + ) + ), + Some("Option[A]"), None) + ), None, 2) + } }