Skip to content

Commit c5cfa0a

Browse files
committed
Fix warning message for matching on redundant nulls
1 parent 8ab7ebe commit c5cfa0a

File tree

3 files changed

+105
-2
lines changed

3 files changed

+105
-2
lines changed

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,12 @@ object SpaceEngine {
923923
&& !sel.tpe.widen.isRef(defn.QuotedExprClass)
924924
&& !sel.tpe.widen.isRef(defn.QuotedTypeClass)
925925

926+
def mayCoverNull(tp: Space)(using Context): Boolean = tp match
927+
case Empty => false
928+
case Prod(_, _, _) => false
929+
case Typ(tp, decomposed) => tp == ConstantType(Constant(null))
930+
case Or(ss) => ss.exists(mayCoverNull)
931+
926932
def checkReachability(m: Match)(using Context): Unit = trace(i"checkReachability($m)"):
927933
val selTyp = toUnderlying(m.selector.tpe).dealias
928934
val isNullable = selTyp.isInstanceOf[FlexibleType] || selTyp.classSymbol.isNullableClass
@@ -948,12 +954,16 @@ object SpaceEngine {
948954
&& !pat.symbol.isAllOf(SyntheticCase, butNot=Method) // ExpandSAMs default cases use SyntheticCase
949955
&& isSubspace(covered, prev)
950956
then
951-
val nullOnly = isNullable && rest.isEmpty && isWildcardArg(pat)
957+
val nullOnly = isNullable && isWildcardArg(pat) && !mayCoverNull(prev)
952958
val msg = if nullOnly then MatchCaseOnlyNullWarning() else MatchCaseUnreachable()
953959
report.warning(msg, pat.srcPos)
954960

955961
// in redundancy check, take guard as false in order to soundly approximate
956-
val newPrev = if guard.isEmpty then covered :: prevs else prevs
962+
val newPrev = if (guard.isEmpty)
963+
then if (isWildcardArg(pat))
964+
then Typ(ConstantType(Constant(null))) :: covered :: prevs
965+
else covered :: prevs
966+
else prevs
957967
recur(rest, newPrev, Nil)
958968

959969
recur(m.cases, Nil, Nil)

tests/warn/redundant-null.check

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:10:7 -----------------------------------------
2+
10 | case _: n.type => // warn
3+
| ^^^^^^^^^
4+
| Unreachable case
5+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:12:7 -----------------------------------------
6+
12 | case _ => // warn
7+
| ^
8+
| Unreachable case
9+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:13:7 -----------------------------------------
10+
13 | case _ => // warn
11+
| ^
12+
| Unreachable case
13+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:18:7 -----------------------------------------
14+
18 | case _ => 3 // warn
15+
| ^
16+
| Unreachable case
17+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:23:7 -----------------------------------------
18+
23 | case _: B => // warn
19+
| ^^^^
20+
| Unreachable case
21+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:24:7 -----------------------------------------
22+
24 | case _ => // warn
23+
| ^
24+
| Unreachable case
25+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:25:7 -----------------------------------------
26+
25 | case null => // warn
27+
| ^^^^
28+
| Unreachable case
29+
-- [E121] Pattern Match Warning: tests/warn/redundant-null.scala:30:7 --------------------------------------------------
30+
30 | case _ => // warn
31+
| ^
32+
| Unreachable case except for null (if this is intentional, consider writing case null => instead).
33+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:31:7 -----------------------------------------
34+
31 | case null => // warn
35+
| ^^^^
36+
| Unreachable case
37+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:32:7 -----------------------------------------
38+
32 | case _ => // warn
39+
| ^
40+
| Unreachable case
41+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:33:7 -----------------------------------------
42+
33 | case _ => // warn
43+
| ^
44+
| Unreachable case
45+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:37:7 -----------------------------------------
46+
37 | case _ => println("unreachable") // warn
47+
| ^
48+
| Unreachable case
49+
-- [E030] Match case Unreachable Warning: tests/warn/redundant-null.scala:41:7 -----------------------------------------
50+
41 | case _ => // warn
51+
| ^
52+
| Unreachable case

tests/warn/redundant-null.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
class A
2+
class B
3+
class C
4+
5+
val n = null
6+
7+
def f(s: A) = s match
8+
case _: n.type =>
9+
case _: A =>
10+
case _: n.type => // warn
11+
case null =>
12+
case _ => // warn
13+
case _ => // warn
14+
15+
def f2(s: A | B | C) = s match
16+
case _: A => 0
17+
case _: C | null | _: B => 1
18+
case _ => 3 // warn
19+
20+
def f3(s: A | B) = s match
21+
case _: A =>
22+
case _ =>
23+
case _: B => // warn
24+
case _ => // warn
25+
case null => // warn
26+
27+
def f4(s: String | Int) = s match
28+
case _: Int =>
29+
case _: String =>
30+
case _ => // warn
31+
case null => // warn
32+
case _ => // warn
33+
case _ => // warn
34+
35+
def f5(x: String) = x match
36+
case x => println("catch all")
37+
case _ => println("unreachable") // warn
38+
39+
def test(s: String | Null) = s match
40+
case ss =>
41+
case _ => // warn

0 commit comments

Comments
 (0)