Skip to content

Commit

Permalink
bugfix: Properly print method type
Browse files Browse the repository at this point in the history
Previously, we would try to find the generic signature of the object we invoked the method on, which would not work in a lot of cases. Now, we find the proper method type.
  • Loading branch information
tgodzik committed Jan 29, 2025
1 parent 0f3f5c4 commit 5e173b5
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,14 @@ final class InferredMethodProvider(
// val list = List(1,2,3)
// list.map(nonExistent)
case (Ident(_)) :: Apply(
Select(Ident(argumentList), _),
selectTree @ Select(qual @ Ident(_), _),
_ :: Nil
) :: _ =>
makeEditsForListApplyWithoutArgs(argumentList, errorMethodName)
makeEditsForListApplyWithoutArgs(
qual,
selectTree,
errorMethodName
)
case _ => unimplemented(errorMethodName)
}
case errorMethod: Select =>
Expand Down Expand Up @@ -292,6 +296,14 @@ final class InferredMethodProvider(
}
}

private def createParameters(params: List[Type]): String = {
params.zipWithIndex
.map { case (p, index) =>
s"arg$index: ${prettyType(p)}"
}
.mkString(", ")
}

private def makeEditsForListApply(
arguments: List[Tree],
containingMethod: Name,
Expand All @@ -309,12 +321,7 @@ final class InferredMethodProvider(
// method1(<<nonExistent>>)
case TypeRef(_, _, args) if definitions.isFunctionType(tpe) =>
val params = args.take(args.size - 1)
val paramsString =
params.zipWithIndex
.map { case (p, index) =>
s"arg$index: ${prettyType(p)}"
}
.mkString(", ")
val paramsString = createParameters(params)
val resultType = args.last
val ret = prettyType(resultType)
signature(
Expand Down Expand Up @@ -353,38 +360,51 @@ final class InferredMethodProvider(
}

private def makeEditsForListApplyWithoutArgs(
argumentList: Name,
qual: Ident,
select: Select,
errorMethodName: String
): Either[String, List[TextEdit]] = {
val listSymbol = context.lookupSymbol(argumentList, _ => true)
if (listSymbol.isSuccess) {
listSymbol.symbol.tpe match {
// need to find the type of the value on which we are mapping
case TypeRef(_, _, TypeRef(_, inputType, _) :: Nil) =>
val paramsString = s"arg0: ${prettyType(inputType.tpe)}"
signature(
name = errorMethodName,
Option(paramsString),
None,
identity,
None
)
case NullaryMethodType(
TypeRef(_, _, TypeRef(_, inputType, _) :: Nil)
) =>
val paramsString = s"arg0: ${prettyType(inputType.tpe)}"

def signatureFromMethodType(sym: Symbol) =
sym.tpe match {
case tpe @ TypeRef(pre, sym, args)
if args.size > 0 && definitions.isFunctionType(tpe) =>
val ret = args.last
val paramsString = createParameters(args.dropRight(1))
val returnTpe =
if (ret.typeSymbol.isTypeParameter) None else Some(prettyType(ret))
signature(
name = errorMethodName,
Option(paramsString),
None,
returnTpe,
identity,
None
)
case _ =>
unimplemented(errorMethodName)
}
} else
unimplemented(errorMethodName)

def findMethodType(tpe: Type): Option[MethodType] = {
tpe match {
case p: PolyType => findMethodType(p.resultType)
case m: MethodType => Some(m)
case _ => None
}
}

val selectTpe = select.tpe
val foundType = if (selectTpe == null) {
val selectQualTpe = context.lookupSymbol(qual.name, _ => true).symbol.tpe
val selectNameSymbol = selectQualTpe.member(select.name)
selectQualTpe.memberType(selectNameSymbol)
} else selectTpe

findMethodType(foundType) match {
case Some(MethodType(List(selectNameSymbol), _)) =>
signatureFromMethodType(selectNameSymbol)
case _ =>
unimplemented(errorMethodName)
}
}

private def makeEditsMethodObject(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ class InsertInferredMethodSuite extends BaseCodeActionSuite {
|""".stripMargin,
"""|trait Main {
| def main() = {
| def otherMethod(arg0: (T1, T2, T3)) = ???
| def otherMethod(arg0: (Int, Int, Int)) = ???
| List((1, 2, 3)).filter(_ => true).map(otherMethod)
| }
|}
Expand All @@ -317,14 +317,13 @@ class InsertInferredMethodSuite extends BaseCodeActionSuite {
|""".stripMargin,
"""|trait Main {
| def main() = {
| def otherMethod(arg0: (T1, T2, T3)) = ???
| def otherMethod(arg0: (Int, Int, Int)) = ???
| List((1, 2, 3)).map(otherMethod)
| }
|}
|""".stripMargin
)

// https://github.com/scalameta/metals/issues/6954
checkEdit(
"lambda-generic-complex-type",
"""|
Expand All @@ -339,7 +338,7 @@ class InsertInferredMethodSuite extends BaseCodeActionSuite {
"""|trait Main {
| def main() = {
| val list = List((1, 2, 3))
| def otherMethod(arg0: (T1, T2, T3)) = ???
| def otherMethod(arg0: (Int, Int, Int)) = ???
| list.map(otherMethod)
| }
|}
Expand All @@ -360,13 +359,40 @@ class InsertInferredMethodSuite extends BaseCodeActionSuite {
"""|trait Main {
| def main() = {
| val list = List(1, 2, 3)
| def otherMethod(arg0: Int) = ???
| def otherMethod(arg0: Int): Boolean = ???
| list.filter(otherMethod)
| }
|}
|""".stripMargin
)

checkEdit(
"method-in-class",
"""|
|class User(i: Int){
| def map(s: String => Int) = i
|}
|trait Main {
| def main() = {
| val user = new User(1)
| user.map(<<otherMethod>>)
| }
|}
|
|""".stripMargin,
"""|class User(i: Int){
| def map(s: String => Int) = i
|}
|trait Main {
| def main() = {
| val user = new User(1)
| def otherMethod(arg0: String): Int = ???
| user.map(otherMethod)
| }
|}
|""".stripMargin
)

checkError(
"lambda-generic-foldLeft",
"""|
Expand Down

0 comments on commit 5e173b5

Please sign in to comment.