diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 0367698b9bbe..484bc88c0983 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -289,7 +289,7 @@ extends NotFoundMsg(MissingIdentID) { } } -class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) +class TypeMismatch(val found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) extends TypeMismatchMsg(found, expected)(TypeMismatchID): def msg(using Context) = diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 9131f4f761a2..0bd407261125 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -221,7 +221,7 @@ object Signatures { val funSymbol = fun.symbol val alternatives = if funSymbol.isLocalToBlock then List(funSymbol.denot) else funSymbol.owner.info.member(funSymbol.name).alternatives - val alternativeIndex = alternatives.map(_.symbol).indexOf(funSymbol) max 0 + val alternativeIndex = bestAlternative(alternatives, params, paramssListIndex) (alternativeIndex, alternatives) if alternativeIndex < alternatives.length then @@ -660,24 +660,56 @@ object Signatures { case msg: NoMatchingOverload => msg.alternatives case _ => Nil - val userParamsTypes = params.map(_.tpe) // Assign a score to each alternative (how many parameters are correct so far), and // use that to determine what is the current active signature. + val alternativeIndex = bestAlternative(alternatives, params, paramssIndex) + (alternativeIndex, alternatives) + } + + /** + * Given a list of alternatives, and a list of parameters, returns the index of the best + * alternative, i.e. the alternative that has the most formal parameters matching the given + * arguments and the least number of formal parameters. + * + * @param alternatives The list of alternatives to inspect. + * @param params The parameters that were given at the call site. + * @param paramssIndex Index of paramss we are currently in. + * + * @return The index of the best alternative. + */ + private def bestAlternative(alternatives: List[SingleDenotation], params: List[tpd.Tree], paramssIndex: Int)(using Context): Int = + val userParamsTypes = params.map( + _.tpe match + case e: PreviousErrorType => + /** + * In case: + * def foo(i: Int, s: String): Unit = ??? + * def foo(i: Boolean, s: Int, x: Double): Unit = ??? + * foo(false, @@) + * + * `false` has error type: `Required: Int, Found: Boolean` + */ + e.msg match + case tm: TypeMismatch => + tm.found + case _ => e + case t => t + ) val alternativesScores = alternatives.map { alt => val alreadyCurriedBonus = if (alt.symbol.paramSymss.length > paramssIndex) 1 else 0 - alt.info.stripPoly match - case tpe: MethodType => alreadyCurriedBonus + - userParamsTypes.zip(tpe.paramInfos).takeWhile{ case (t0, t1) => t0 <:< t1 }.size - case _ => 0 + alt.info.stripPoly match + case tpe: MethodType => + val score = alreadyCurriedBonus + + userParamsTypes + .zip(tpe.paramInfos) + .takeWhile { case (t0, t1) =>t0 <:< t1 } + .size + (score, -tpe.paramInfos.length) + case _ => (0, 0) } - - val bestAlternative = - if (alternativesScores.isEmpty) 0 - else alternativesScores.zipWithIndex.maxBy(_._1)._2 - - (bestAlternative, alternatives) - } + if (alternativesScores.isEmpty) 0 + else alternativesScores.zipWithIndex.maxBy(_._1)._2 } diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala index 01d5e03b6c1e..ac63ef92aef5 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpSuite.scala @@ -1499,3 +1499,37 @@ class SignatureHelpSuite extends BaseSignatureHelpSuite: | ^ |""".stripMargin ) + + @Test def `correct-alternative` = + check( + """ + |object x { + | def foo(i: Int, s: String): Unit = ??? + | def foo(i: Boolean, s: Int, x: Double): Unit = ??? + | + | foo(false, @@) + |} + |""".stripMargin, + """ + |foo(i: Boolean, s: Int, x: Double): Unit + | ^^^^^^ + |foo(i: Int, s: String): Unit + |""".stripMargin + ) + + @Test def `correct-alternative1` = + check( + """ + |object x { + | def foo(i: Boolean, s: String)(b: Int): Unit = ??? + | def foo(i: Boolean, s: Int)(b: String): Unit = ??? + | + | foo(false, 123)(@@) + |} + |""".stripMargin, + """ + |foo(i: Boolean, s: Int)(b: String): Unit + | ^^^^^^^^^ + |foo(i: Boolean, s: String)(b: Int): Unit + |""".stripMargin + )