Skip to content

Commit 90a3db7

Browse files
ghostbuster91ckipp01
authored andcommitted
Add actionable item to PatternMatchExhaustivity diag
1 parent 2f20e08 commit 90a3db7

File tree

4 files changed

+56
-11
lines changed

4 files changed

+56
-11
lines changed

Diff for: compiler/src/dotty/tools/dotc/reporting/messages.scala

+24-2
Original file line numberDiff line numberDiff line change
@@ -844,10 +844,12 @@ extends Message(LossyWideningConstantConversionID):
844844
|Write `.to$targetType` instead."""
845845
def explain(using Context) = ""
846846

847-
class PatternMatchExhaustivity(uncoveredFn: => String, hasMore: Boolean)(using Context)
847+
class PatternMatchExhaustivity(val uncovered: Seq[String], tree: untpd.Match)(using Context)
848848
extends Message(PatternMatchExhaustivityID) {
849849
def kind = MessageKind.PatternMatchExhaustivity
850-
lazy val uncovered = uncoveredFn
850+
851+
private val hasMore = uncovered.lengthCompare(6) > 0
852+
851853
def msg(using Context) =
852854
val addendum = if hasMore then "(More unmatched cases are elided)" else ""
853855
i"""|${hl("match")} may not be exhaustive.
@@ -862,6 +864,26 @@ extends Message(PatternMatchExhaustivityID) {
862864
| - If an extractor always return ${hl("Some(...)")}, write ${hl("Some[X]")} for its return type
863865
| - Add a ${hl("case _ => ...")} at the end to match all remaining cases
864866
|"""
867+
868+
override def actions(using Context) =
869+
import scala.language.unsafeNulls
870+
val endPos = tree.cases.lastOption.map(_.endPos).getOrElse(tree.selector.endPos)
871+
val startColumn = tree.cases.lastOption.map(_.startPos.startColumn).getOrElse(tree.selector.startPos.startColumn + 2)
872+
val pathes = List(
873+
ActionPatch(endPos, uncovered.map(c=> indent(s"case $c => ???", startColumn)).mkString("\n", "\n", "")),
874+
)
875+
List(
876+
CodeAction(title = s"Insert missing cases (${uncovered.size})",
877+
description = None,
878+
patches = pathes
879+
)
880+
)
881+
882+
883+
private def indent(text:String, margin: Int): String = {
884+
import scala.language.unsafeNulls
885+
" " * margin + text
886+
}
865887
}
866888

867889
class UncheckedTypePattern(msgFn: => String)(using Context)

Diff for: compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

+3-4
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,7 @@ object SpaceEngine {
775775
checkConstraint(genConstraint(sp))(using ctx.fresh.setNewTyperState())
776776
}
777777

778-
def showSpaces(ss: Seq[Space])(using Context): String = ss.map(show).mkString(", ")
778+
def showSpaces(ss: Seq[Space])(using Context): Seq[String] = ss.map(show)
779779

780780
/** Display spaces */
781781
def show(s: Space)(using Context): String = {
@@ -900,9 +900,8 @@ object SpaceEngine {
900900

901901

902902
if uncovered.nonEmpty then
903-
val hasMore = uncovered.lengthCompare(6) > 0
904-
val deduped = dedup(uncovered.take(6))
905-
report.warning(PatternMatchExhaustivity(showSpaces(deduped), hasMore), m.selector)
903+
val deduped = dedup(uncovered)
904+
report.warning(PatternMatchExhaustivity(showSpaces(deduped), m), m.selector)
906905
}
907906

908907
private def redundancyCheckable(sel: Tree)(using Context): Boolean =

Diff for: compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala

+28-4
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,31 @@ class CodeActionTest extends DottyTest:
5252
// TODO look into trying to remove the extra space that is left behind
5353
"""|final class Test
5454
|""".stripMargin
55+
)
5556

57+
@Test def insertMissingCases =
58+
checkCodeAction(
59+
code = """|enum Tree:
60+
| case Node(l: Tree, r: Tree)
61+
| case Leaf(v: String)
62+
|
63+
|object Test:
64+
| def foo(tree: Tree) = tree match {
65+
| case Tree.Node(_, _) => ???
66+
| }
67+
|""".stripMargin,
68+
title = "Insert missing cases (1)",
69+
expected = """|enum Tree:
70+
| case Node(l: Tree, r: Tree)
71+
| case Leaf(v: String)
72+
|
73+
|object Test:
74+
| def foo(tree: Tree) = tree match {
75+
| case Tree.Node(_, _) => ???
76+
| case Tree.Leaf(_) => ???
77+
| }
78+
|""".stripMargin,
79+
afterPhase = "patternMatcher"
5680
)
5781

5882
// Make sure we're not using the default reporter, which is the ConsoleReporter,
@@ -61,16 +85,16 @@ class CodeActionTest extends DottyTest:
6185
val rep = new StoreReporter(null) with UniqueMessagePositions with HideNonSensicalMessages
6286
initialCtx.setReporter(rep).withoutColors
6387

64-
private def checkCodeAction(code: String, title: String, expected: String) =
88+
private def checkCodeAction(code: String, title: String, expected: String, afterPhase: String = "typer") =
6589
ctx = newContext
6690
val source = SourceFile.virtual("test", code).content
67-
val runCtx = checkCompile("typer", code) { (_, _) => () }
91+
val runCtx = checkCompile(afterPhase, code) { (_, _) => () }
6892
val diagnostics = runCtx.reporter.removeBufferedMessages
69-
assertEquals(1, diagnostics.size)
93+
assertEquals("Expected exactly one diagnostic", 1, diagnostics.size)
7094

7195
val diagnostic = diagnostics.head
7296
val actions = diagnostic.msg.actions.toList
73-
assertEquals(1, actions.size)
97+
assertEquals("Expected exactly one action", 1, actions.size)
7498

7599
// TODO account for more than 1 action
76100
val action = actions.head

Diff for: compiler/test/dotty/tools/dotc/reporting/TestReporter.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M
7575

7676
// Here we add extra information that we should know about the error message
7777
val extra = dia.msg match {
78-
case pm: PatternMatchExhaustivity => s": ${pm.uncovered}"
78+
case pm: PatternMatchExhaustivity => s": ${pm.uncovered.mkString("\n")}"
7979
case _ => ""
8080
}
8181

0 commit comments

Comments
 (0)