Skip to content

Commit

Permalink
Add pattern completion for unapply contexts (#20274)
Browse files Browse the repository at this point in the history
Fixes #19972.

Add pattern completion for `Unapply` tree contexts.

A typical example would be
```scala
optionList match
    case List(S@@)
```
which should be prompted `Some(value)`, due to `List.unapplySeq`
expecting `Option[T]` patterns as arguments.
  • Loading branch information
natsukagami committed May 22, 2024
1 parent 4636ce2 commit 5c761fd
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class Completions(
false
case (_: (Import | Export)) :: _ => false
case _ :: (_: (Import | Export)) :: _ => false
// UnApply has patterns included in MatchCaseCompletions
case _ :: (_: UnApply) :: _ => false
case _ => true

private lazy val isNew: Boolean = Completion.isInNewContext(adjustedPath)
Expand Down Expand Up @@ -405,6 +407,36 @@ class Completions(
true,
)

// unapply pattern
case Ident(name) :: (unapp : UnApply) :: _ =>
(
CaseKeywordCompletion.contribute(
EmptyTree, // no selector
completionPos,
indexedContext,
config,
search,
parent = unapp,
autoImports,
patternOnly = Some(name.decoded)
),
false,
)
case Select(_, name) :: (unapp : UnApply) :: _ =>
(
CaseKeywordCompletion.contribute(
EmptyTree, // no selector
completionPos,
indexedContext,
config,
search,
parent = unapp,
autoImports,
patternOnly = Some(name.decoded)
),
false,
)

// class FooImpl extends Foo:
// def x|
case OverrideExtractor(td, completing, start, exhaustive, fallbackName) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import dotty.tools.dotc.core.Types.NoType
import dotty.tools.dotc.core.Types.OrType
import dotty.tools.dotc.core.Types.Type
import dotty.tools.dotc.core.Types.TypeRef
import dotty.tools.dotc.core.Types.AppliedType
import dotty.tools.dotc.typer.Applications.UnapplyArgs
import dotty.tools.dotc.util.SourcePosition
import dotty.tools.pc.AutoImports.AutoImportsGenerator
import dotty.tools.pc.AutoImports.SymbolImport
Expand Down Expand Up @@ -75,10 +77,24 @@ object CaseKeywordCompletion:
patternOnly,
hasBind
)

val printer = ShortenedTypePrinter(search, IncludeDefaultParam.Never)(using indexedContext)
val selTpe = selector match
case EmptyTree =>
parent match
/* Parent is an unapply pattern */
case UnApply(fn, implicits, patterns) if !fn.tpe.isErroneous =>
patternOnly match
case None => None
case Some(value) =>
val argPts = UnapplyArgs(fn.tpe.widen.finalResultType, fn, patterns, parent.srcPos).argTypes
patterns.zipWithIndex
.find:
case (Ident(v), tpe) => v.decoded == value
case (Select(_, v), tpe) => v.decoded == value
case t => false
.map((_, id) => argPts(id).widen.deepDealias)
/* Parent is a function expecting a case match expression */
case TreeApply(fun, _) if !fun.tpe.isErroneous =>
fun.tpe.paramInfoss match
case (head :: Nil) :: _
Expand All @@ -105,7 +121,8 @@ object CaseKeywordCompletion:
if patternOnly.isEmpty then
val selectorTpe = selTpe.show
val tpeLabel =
if !selectorTpe.contains("x$1") then selectorTpe
if !selectorTpe.contains("x$1") /* selector of a function type? */ then
selectorTpe
else selector.symbol.info.show
val label = s"case ${tpeLabel} =>"
List(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,14 +634,58 @@ class CompletionSuite extends BaseCompletionSuite:
|""".stripMargin
)

@Test def patRecursive =
check(
s"""|object Main {
| Option(List(Option(1))) match {
| case Some(List(None, Som@@))
|}
|""".stripMargin,
"""|Some(value) scala
|Some scala
|""".stripMargin
)
check(
s"""|object Main {
| (null: Option[Option[Option[Option[Int]]]]) match
| case Some(Some(Some(Som@@))))
|}
|""".stripMargin,
"""|Some(value) scala
|Some scala
|""".stripMargin
)
check(
s"""|object Main {
| Option(Option(1)) match {
| case Some(Som@@)
|}
|""".stripMargin,
"""|Some(value) scala
|Some scala
|""".stripMargin
)
check(
s"""|object Test:
| case class NestedClass(x: Int)
|object TestRun:
| Option(Test.NestedClass(5)) match
| case Some(Test.Neste@@)
|""".stripMargin,
"""|NestedClass(x) test.Test
|NestedClass test.Test
|""".stripMargin
)

@Test def pat1 =
check(
s"""|object Main {
| Option(1) match {
| case List(Som@@)
|}
|""".stripMargin,
"""|Some[A](value: A): Some[A]
"""|Some(value) scala
|Some scala
|Some scala
|""".stripMargin
)
Expand Down

0 comments on commit 5c761fd

Please sign in to comment.