Skip to content

Commit 14e806b

Browse files
authored
Merge pull request #14639 from rochala/match-type-completion
add completions for specific MatchType cases
2 parents bfd4b53 + 6aa2bdf commit 14e806b

File tree

3 files changed

+121
-18
lines changed

3 files changed

+121
-18
lines changed

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,14 +310,22 @@ object Completion {
310310
resultMappings
311311
}
312312

313+
/** Replaces underlying type with reduced one, when it's MatchType */
314+
def reduceUnderlyingMatchType(qual: Tree)(using Context): Tree=
315+
qual.tpe.widen match
316+
case ctx.typer.MatchTypeInDisguise(mt) => qual.withType(mt)
317+
case _ => qual
318+
313319
/** Completions for selections from a term.
314320
* Direct members take priority over members from extensions
315321
* and so do members from extensions over members from implicit conversions
316322
*/
317323
def selectionCompletions(qual: Tree)(using Context): CompletionMap =
318-
implicitConversionMemberCompletions(qual) ++
319-
extensionCompletions(qual) ++
320-
directMemberCompletions(qual)
324+
val reducedQual = reduceUnderlyingMatchType(qual)
325+
326+
implicitConversionMemberCompletions(reducedQual) ++
327+
extensionCompletions(reducedQual) ++
328+
directMemberCompletions(reducedQual)
321329

322330
/** Completions for members of `qual`'s type.
323331
* These include inherited definitions but not members added by extensions or implicit conversions

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

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,22 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
15151515
assignType(cpy.Closure(tree)(env1, meth1, target), meth1, target)
15161516
}
15171517

1518+
/** Extractor for match types hidden behind an AppliedType/MatchAlias */
1519+
object MatchTypeInDisguise {
1520+
def unapply(tp: AppliedType)(using Context): Option[MatchType] = tp match {
1521+
case AppliedType(tycon: TypeRef, args) =>
1522+
tycon.info match {
1523+
case MatchAlias(alias) =>
1524+
alias.applyIfParameterized(args) match {
1525+
case mt: MatchType => Some(mt)
1526+
case _ => None
1527+
}
1528+
case _ => None
1529+
}
1530+
case _ => None
1531+
}
1532+
}
1533+
15181534
def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree =
15191535
tree.selector match {
15201536
case EmptyTree =>
@@ -1542,21 +1558,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
15421558
val selType = rawSelectorTpe match
15431559
case c: ConstantType if tree.isInline => c
15441560
case otherTpe => otherTpe.widen
1545-
/** Extractor for match types hidden behind an AppliedType/MatchAlias */
1546-
object MatchTypeInDisguise {
1547-
def unapply(tp: AppliedType): Option[MatchType] = tp match {
1548-
case AppliedType(tycon: TypeRef, args) =>
1549-
tycon.info match {
1550-
case MatchAlias(alias) =>
1551-
alias.applyIfParameterized(args) match {
1552-
case mt: MatchType => Some(mt)
1553-
case _ => None
1554-
}
1555-
case _ => None
1556-
}
1557-
case _ => None
1558-
}
1559-
}
15601561

15611562
/** Does `tree` has the same shape as the given match type?
15621563
* We only support typed patterns with empty guards, but

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

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,4 +1130,98 @@ class CompletionTest {
11301130
code"""import scala.util.chaining.`s${m1}"""
11311131
.withSource.completion(m1, expected)
11321132
}
1133+
1134+
@Test def matchTypeCompletions: Unit = {
1135+
val expected = Set(
1136+
("fooTest", Method, "(y: Int): Int"),
1137+
)
1138+
code"""case class Foo(x: Int) {
1139+
| def fooTest(y: Int): Int = ???
1140+
|}
1141+
|type Elem[X] = X match {
1142+
| case Int => Foo
1143+
| case Any => X
1144+
|}
1145+
|def elem[X](x: X): Elem[X] = x match {
1146+
| case x: Int => Foo(x)
1147+
| case x: Any => x
1148+
|}
1149+
|object Test:
1150+
| elem(1).foo${m1}"""
1151+
.withSource.completion(m1, expected)
1152+
}
1153+
1154+
@Test def higherKindedMatchTypeDeclaredCompletion: Unit = {
1155+
val expected = Set(
1156+
("map", Method, "[B](f: Int => B): Foo[B]"),
1157+
)
1158+
code"""trait Foo[A] {
1159+
| def map[B](f: A => B): Foo[B] = ???
1160+
|}
1161+
|case class Bar[F[_]](bar: F[Int])
1162+
|type M[T] = T match {
1163+
| case Int => Foo[Int]
1164+
|}
1165+
|object Test:
1166+
| val x = Bar[M](new Foo[Int]{})
1167+
| x.bar.m${m1}"""
1168+
.withSource.completion(m1, expected)
1169+
}
1170+
1171+
@Test def higherKindedMatchTypeLazyCompletion: Unit = {
1172+
val expected = Set(
1173+
("map", Method, "[B](f: Int => B): Foo[B]"),
1174+
)
1175+
code"""trait Foo[A] {
1176+
| def map[B](f: A => B): Foo[B] = ???
1177+
|}
1178+
|case class Bar[F[_]](bar: F[Int])
1179+
|type M[T] = T match {
1180+
| case Int => Foo[Int]
1181+
|}
1182+
|def foo(x: Bar[M]) = x.bar.m${m1}"""
1183+
.withSource.completion(m1, expected)
1184+
}
1185+
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+
// }
1207+
1208+
@Test def higherKindedMatchTypeExtensionMethodCompletion: Unit = {
1209+
val expected = Set(
1210+
("mapFoo", Method, "[B](f: Int => B): Foo[B]"),
1211+
("mapExtensionMethod", Method, "[B](f: Int => B): Foo[B]"),
1212+
)
1213+
code"""trait Foo[A] {
1214+
| def mapFoo[B](f: A => B): Foo[B] = ???
1215+
|}
1216+
|extension[A] (x: Foo[A]) {
1217+
| def mapExtensionMethod[B](f: A => B): Foo[B] = ???
1218+
|}
1219+
|case class Baz[F[_]](baz: F[Int])
1220+
|type M[T] = T match {
1221+
| case Int => Foo[Int]
1222+
|}
1223+
|case class Bar[F[_]](bar: F[Int])
1224+
|def foo(x: Bar[M]) = x.bar.ma${m1}"""
1225+
.withSource.completion(m1, expected)
1226+
}
11331227
}

0 commit comments

Comments
 (0)