Skip to content

Commit 6b143f1

Browse files
committed
fix incorrect type parameters for completions
1 parent af34319 commit 6b143f1

File tree

3 files changed

+95
-37
lines changed

3 files changed

+95
-37
lines changed

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -381,9 +381,10 @@ object Completion {
381381
if qual.tpe.widenDealias.isExactlyNothing || qual.tpe.isNullType then
382382
Map.empty
383383
else
384-
val membersFromConversion =
385-
implicitConversionTargets(qual)(using ctx.fresh.setExploreTyperState()).flatMap(accessibleMembers)
386-
membersFromConversion.toSeq.groupByName
384+
implicitConversionTargets(qual)(using ctx.fresh.setExploreTyperState())
385+
.flatMap(accessibleMembers)
386+
.toSeq
387+
.groupByName
387388

388389
/** Completions from extension methods */
389390
private def extensionCompletions(qual: Tree)(using Context): CompletionMap =
@@ -489,17 +490,19 @@ object Completion {
489490

490491
/**
491492
* Given `qual` of type T, finds all the types S such that there exists an implicit conversion
492-
* from T to S.
493+
* from T to S. It then applies conversion method for proper type parameter resolution.
493494
*
494495
* @param qual The argument to which the implicit conversion should be applied.
495-
* @return The set of types that `qual` can be converted to.
496+
* @return Types after implicit conversion.
496497
*/
497498
private def implicitConversionTargets(qual: Tree)(using Context): Set[Type] = {
498499
val typer = ctx.typer
499500
val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span).allImplicits
500-
val targets = conversions.map(_.widen.finalResultType)
501+
val convertedTrees = conversions.flatMap(typer.tryApplyingImplicitConversion(_, qual))
502+
val targets = convertedTrees.map(_.tpe.finalResultType)
503+
501504
interactiv.println(i"implicit conversion targets considered: ${targets.toList}%, %")
502-
targets
505+
targets
503506
}
504507

505508
/** Filter for names that should appear when looking for completions. */

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2289,6 +2289,27 @@ trait Applications extends Compatibility {
22892289
catch
22902290
case NonFatal(_) => None
22912291

2292+
/** Tries applying conversion method reference to a provided receiver
2293+
*
2294+
* returns converted tree in case of success.
2295+
* None is returned if conversion method application fails.
2296+
*/
2297+
def tryApplyingImplicitConversion(conversionMethodRef: TermRef, receiver: Tree)(using Context): Option[Tree] =
2298+
val conversionMethodTree = ref(conversionMethodRef, needLoad = false)
2299+
val newCtx = ctx.fresh.setNewScope.setReporter(new reporting.ThrowingReporter(ctx.reporter))
2300+
2301+
try
2302+
val appliedTree = inContext(newCtx) {
2303+
newCtx.typer.typed(untpd.Apply(conversionMethodTree, (receiver:: Nil)))
2304+
}
2305+
2306+
if appliedTree.tpe.exists && !appliedTree.tpe.isError then
2307+
Some(appliedTree)
2308+
else
2309+
None
2310+
catch
2311+
case NonFatal(_) => None
2312+
22922313
def isApplicableExtensionMethod(methodRef: TermRef, receiverType: Type)(using Context): Boolean =
22932314
methodRef.symbol.is(ExtensionMethod) && !receiverType.isBottomType &&
22942315
tryApplyingExtensionMethod(methodRef, nullLiteral.asInstance(receiverType)).nonEmpty

language-server/test/dotty/tools/languageserver/CompletionTest.scala

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,7 +1001,7 @@ class CompletionTest {
10011001
)
10021002
code"""import scala.concurrent.Future
10031003
|class Foo(x: Fut${m1})""".withSource
1004-
.completion(m1, expected)
1004+
.completion(m1, expected)
10051005
}
10061006

10071007
@Test def completeTemplateParents: Unit = {
@@ -1011,7 +1011,7 @@ class CompletionTest {
10111011
)
10121012
code"""import scala.concurrent.Future
10131013
|class Foo extends Futu${m1}""".withSource
1014-
.completion(m1, expected)
1014+
.completion(m1, expected)
10151015
}
10161016

10171017
@Test def completeTemplateSelfType: Unit = {
@@ -1021,7 +1021,7 @@ class CompletionTest {
10211021
)
10221022
code"""import scala.concurrent.Future
10231023
|class Foo[A]{ self: Futu${m1} => }""".withSource
1024-
.completion(m1, expected)
1024+
.completion(m1, expected)
10251025
}
10261026

10271027
@Test def backticks: Unit = {
@@ -1061,7 +1061,7 @@ class CompletionTest {
10611061
| enum Bar:
10621062
| case `back-tick`
10631063
| case `match`
1064-
|
1064+
|
10651065
| val x = Bar.${m1}"""
10661066
.withSource.completion(m1, expected)
10671067
}
@@ -1074,7 +1074,7 @@ class CompletionTest {
10741074
| enum Bar:
10751075
| case `back-tick`
10761076
| case `match`
1077-
|
1077+
|
10781078
| val x = Bar.`back${m1}"""
10791079
.withSource.completion(m1, expected)
10801080
}
@@ -1086,7 +1086,7 @@ class CompletionTest {
10861086
code"""object Foo:
10871087
| enum Bar:
10881088
| case `has space`
1089-
|
1089+
|
10901090
| val x = Bar.`has s${m1}"""
10911091
.withSource.completion(m1, expected)
10921092
}
@@ -1102,7 +1102,7 @@ class CompletionTest {
11021102
| val foo = 1
11031103
| val `foo-bar` = 2
11041104
| val `bar` = 3
1105-
|
1105+
|
11061106
| val x = Bar.fo${m1}"""
11071107
.withSource.completion(m1, expected)
11081108
}
@@ -1117,7 +1117,7 @@ class CompletionTest {
11171117
| object Bar:
11181118
| val foo = 1
11191119
| val `foo-bar` = 2
1120-
|
1120+
|
11211121
| val x = Bar.`fo${m1}"""
11221122
.withSource.completion(m1, expected)
11231123
}
@@ -1146,7 +1146,7 @@ class CompletionTest {
11461146
| case x: Int => Foo(x)
11471147
| case x: Any => x
11481148
|}
1149-
|object Test:
1149+
|object Test:
11501150
| elem(1).foo${m1}"""
11511151
.withSource.completion(m1, expected)
11521152
}
@@ -1183,27 +1183,61 @@ class CompletionTest {
11831183
.withSource.completion(m1, expected)
11841184
}
11851185

1186-
// This test is not passing due to https://github.com/lampepfl/dotty/issues/14687
1187-
// @Test def higherKindedMatchTypeImplicitConversionCompletion: Unit = {
1188-
// val expected = Set(
1189-
// ("mapBoo", Method, "[B](op: Int => B): Boo[B]"),
1190-
// ("mapFoo", Method, "[B](op: Int => B): Foo[B]"),
1191-
// )
1192-
// code"""import scala.language.implicitConversions
1193-
// |case class Foo[A](x: A) {
1194-
// | def mapFoo[B](op: A => B): Foo[B] = ???
1195-
// |}
1196-
// |case class Boo[A](x: A) {
1197-
// | def mapBoo[B](op: A => B): Boo[B] = ???
1198-
// |}
1199-
// |type M[A] = A match {
1200-
// | case Int => Foo[Int]
1201-
// |}
1202-
// |implicit def fooToBoo[A](x: Foo[A]): Boo[A] = Boo(x.x)
1203-
// |case class Bar[F[_]](bar: F[Int])
1204-
// |def foo(x: Bar[M]) = x.bar.m${m1}"""
1205-
// .withSource.completion(m1, expected)
1206-
// }
1186+
@Test def higherKindedMatchTypeImplicitConversionCompletion: Unit = {
1187+
val expected = Set(
1188+
("mapBoo", Method, "[B](op: Int => B): Boo[B]"),
1189+
("mapFoo", Method, "[B](op: Int => B): Foo[B]"),
1190+
)
1191+
code"""import scala.language.implicitConversions
1192+
|case class Foo[A](x: A) {
1193+
| def mapFoo[B](op: A => B): Foo[B] = ???
1194+
|}
1195+
|case class Boo[A](x: A) {
1196+
| def mapBoo[B](op: A => B): Boo[B] = ???
1197+
|}
1198+
|type M[A] = A match {
1199+
| case Int => Foo[Int]
1200+
|}
1201+
|implicit def fooToBoo[A](x: Foo[A]): Boo[A] = Boo(x.x)
1202+
|case class Bar[F[_]](bar: F[Int])
1203+
|def foo(x: Bar[M]) = x.bar.m${m1}"""
1204+
.withSource.completion(m1, expected)
1205+
}
1206+
1207+
@Test def higherKindedTypeInferenceTest: Unit = {
1208+
val expected = Set(
1209+
("fooTest", Method, "(x: Int): String"),
1210+
)
1211+
code"""class Test[A, B] {
1212+
| def fooTest(x: A): B = ???
1213+
|}
1214+
|
1215+
|object M:
1216+
| val test = new Test[Int, String] {}
1217+
| test.foo${m1}
1218+
| (new Test[Int, String] {}).foo${m2}""".withSource
1219+
.completion(m1, expected)
1220+
.completion(m2, expected)
1221+
}
1222+
1223+
@Test def higherKindedImplicitConversionsCompletions: Unit = {
1224+
val expected = Set(
1225+
("mapBoo", Method, "[B](f: Int => B): Boo[B]"),
1226+
("mapFoo", Method, "[B](f: Int => B): Foo[B]"),
1227+
)
1228+
code"""import scala.language.implicitConversions
1229+
|case class Foo[A](x: A) {
1230+
| def mapFoo[B](f: A => B): Foo[B] = ???
1231+
|}
1232+
|case class Boo[C](x: C) {
1233+
| def mapBoo[B](f: C => B): Boo[B] = ???
1234+
}
1235+
|implicit def fooToBoo[D](x: Foo[D]): Boo[D] = Boo(x.x)
1236+
|object Test:
1237+
| val x = Foo(1)
1238+
| x.ma${m1}"""
1239+
.withSource.completion(m1, expected)
1240+
}
12071241

12081242
@Test def higherKindedMatchTypeExtensionMethodCompletion: Unit = {
12091243
val expected = Set(

0 commit comments

Comments
 (0)