From 210b2d1eb859a1f3350bebbf9ca1b36f2dc44861 Mon Sep 17 00:00:00 2001 From: HarrisL2 Date: Fri, 20 Sep 2024 23:23:39 -0400 Subject: [PATCH] Improve warning for wildcard matching non-nullable types (scala#21577) Adds a more detailed warning message when a wildcard case is unreachable on an non-nullable type to suggest adding a nullable type annotation. Fixes scala#21577 --- .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 6 +++++ .../tools/dotc/transform/patmat/Space.scala | 6 ++++- .../dotty/tools/dotc/CompilationTests.scala | 5 ++++ tests/explicit-nulls/warn/i21577.check | 8 +++++++ tests/explicit-nulls/warn/i21577.scala | 24 +++++++++++++++++++ 6 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/explicit-nulls/warn/i21577.check create mode 100644 tests/explicit-nulls/warn/i21577.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index cb5e8a7b314c..806ae4b11e26 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -215,6 +215,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case TailrecNestedCallID //errorNumber: 199 case FinalLocalDefID // errorNumber: 200 case NonNamedArgumentInJavaAnnotationID // errorNumber: 201 + case MatchCaseNonNullableWildcardWarningID // errorNumber: 202 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 01eb2acfa4de..114d10762898 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -935,6 +935,12 @@ extends PatternMatchMsg(MatchCaseOnlyNullWarningID) { def explain(using Context) = "" } +class MatchCaseNonNullableWildcardWarning()(using Context) +extends PatternMatchMsg(MatchCaseNonNullableWildcardWarningID) { + def msg(using Context) = i"""Unreachable case (if expression is expected to be nullable, consider giving it a nullable type annotation).""" + def explain(using Context) = "" +} + class MatchableWarning(tp: Type, pattern: Boolean)(using Context) extends TypeMsg(MatchableWarningID) { def msg(using Context) = diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 20b0099d82e2..914c831d7916 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -928,7 +928,11 @@ object SpaceEngine { && isSubspace(covered, prev) then { val nullOnly = isNullable && i == len - 1 && isWildcardArg(pat) - val msg = if nullOnly then MatchCaseOnlyNullWarning() else MatchCaseUnreachable() + val wildcardNotNullable = i == len - 1 && isWildcardArg(pat) + val msg = + if nullOnly then MatchCaseOnlyNullWarning() + else if wildcardNotNullable then MatchCaseNonNullableWildcardWarning() + else MatchCaseUnreachable() report.warning(msg, pat.srcPos) } deferred = Nil diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 0c5d5764949a..dca6b960d90f 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -212,6 +212,11 @@ class CompilationTests { ) }.checkCompile() + @Test def explicitNullsWarn: Unit = { + implicit val testGroup: TestGroup = TestGroup("explicitNullsWarn") + compileFilesInDir("tests/explicit-nulls/warn", explicitNullsOptions) + }.checkWarnings() + @Test def explicitNullsRun: Unit = { implicit val testGroup: TestGroup = TestGroup("explicitNullsRun") compileFilesInDir("tests/explicit-nulls/run", explicitNullsOptions) diff --git a/tests/explicit-nulls/warn/i21577.check b/tests/explicit-nulls/warn/i21577.check new file mode 100644 index 000000000000..2ab00ff1cf5e --- /dev/null +++ b/tests/explicit-nulls/warn/i21577.check @@ -0,0 +1,8 @@ +-- [E202] Pattern Match Warning: tests/explicit-nulls/warn/i21577.scala:5:9 -------------------------------------------- +5 | case _ => println(2) // warn + | ^ + | Unreachable case (if expression is expected to be nullable, consider giving it a nullable type annotation). +-- [E202] Pattern Match Warning: tests/explicit-nulls/warn/i21577.scala:12:11 ------------------------------------------ +12 | case _ => println(2) // warn + | ^ + | Unreachable case (if expression is expected to be nullable, consider giving it a nullable type annotation). diff --git a/tests/explicit-nulls/warn/i21577.scala b/tests/explicit-nulls/warn/i21577.scala new file mode 100644 index 000000000000..145a02dad4c1 --- /dev/null +++ b/tests/explicit-nulls/warn/i21577.scala @@ -0,0 +1,24 @@ +def f(s: String) = + val s2 = s.trim() + s2 match + case s3: String => println(1) + case _ => println(2) // warn + + +def f2(s: String | Null) = + val s2 = s.nn.trim() + s2 match + case s3: String => println(1) + case _ => println(2) // warn + +def f3(s: String | Null) = + val s2 = s + s2 match + case s3: String => println(1) + case _ => println(2) + +def f4(s: String | Int) = + val s2 = s + s2 match + case s3: String => println(1) + case _ => println(2) \ No newline at end of file