Skip to content

Commit 3eb9e09

Browse files
committed
Add actionable item to PatternMatchExhaustivity diag
1 parent e97fea5 commit 3eb9e09

File tree

4 files changed

+57
-12
lines changed

4 files changed

+57
-12
lines changed

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -840,10 +840,12 @@ extends Message(LossyWideningConstantConversionID):
840840
|Write `.to$targetType` instead."""
841841
def explain(using Context) = ""
842842

843-
class PatternMatchExhaustivity(uncoveredFn: => String, hasMore: Boolean)(using Context)
843+
class PatternMatchExhaustivity(val uncovered: Seq[String], tree: untpd.Match)(using Context)
844844
extends Message(PatternMatchExhaustivityID) {
845845
def kind = MessageKind.PatternMatchExhaustivity
846-
lazy val uncovered = uncoveredFn
846+
847+
private val hasMore = uncovered.lengthCompare(6) > 0
848+
847849
def msg(using Context) =
848850
val addendum = if hasMore then "(More unmatched cases are elided)" else ""
849851
i"""|${hl("match")} may not be exhaustive.
@@ -858,6 +860,26 @@ extends Message(PatternMatchExhaustivityID) {
858860
| - If an extractor always return ${hl("Some(...)")}, write ${hl("Some[X]")} for its return type
859861
| - Add a ${hl("case _ => ...")} at the end to match all remaining cases
860862
|"""
863+
864+
override def actions(using Context) =
865+
import scala.language.unsafeNulls
866+
val endPos = tree.cases.lastOption.map(_.endPos).getOrElse(tree.selector.endPos)
867+
val startColumn = tree.cases.lastOption.map(_.startPos.startColumn).getOrElse(tree.selector.startPos.startColumn + 2)
868+
val pathes = List(
869+
ActionPatch(endPos, uncovered.map(c=> indent(s"case $c => ???", startColumn)).mkString("\n", "\n", "")),
870+
)
871+
List(
872+
CodeAction(title = s"Insert missing cases (${uncovered.size})",
873+
description = None,
874+
patches = pathes
875+
)
876+
)
877+
878+
879+
private def indent(text:String, margin: Int): String = {
880+
import scala.language.unsafeNulls
881+
" " * margin + text
882+
}
861883
}
862884

863885
class UncheckedTypePattern(msgFn: => String)(using Context)
@@ -2985,4 +3007,4 @@ class ImplausiblePatternWarning(pat: tpd.Tree, selType: Type)(using Context)
29853007
i"""|Implausible pattern:
29863008
|$pat could match selector of type $selType
29873009
|only if there is an `equals` method identifying elements of the two types."""
2988-
def explain(using Context) = ""
3010+
def explain(using Context) = ""

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ object SpaceEngine {
769769
checkConstraint(genConstraint(sp))(using ctx.fresh.setNewTyperState())
770770
}
771771

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

774774
/** Display spaces */
775775
def show(s: Space)(using Context): String = {
@@ -894,9 +894,8 @@ object SpaceEngine {
894894

895895

896896
if uncovered.nonEmpty then
897-
val hasMore = uncovered.lengthCompare(6) > 0
898-
val deduped = dedup(uncovered.take(6))
899-
report.warning(PatternMatchExhaustivity(showSpaces(deduped), hasMore), m.selector)
897+
val deduped = dedup(uncovered)
898+
report.warning(PatternMatchExhaustivity(showSpaces(deduped), m), m.selector)
900899
}
901900

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

compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala

Lines changed: 28 additions & 4 deletions
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

compiler/test/dotty/tools/dotc/reporting/TestReporter.scala

Lines changed: 1 addition & 1 deletion
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)