@@ -116,6 +116,7 @@ object SpaceEngine {
116116 def isSubspace (a : Space , b : Space )(using Context ): Boolean = a.isSubspace(b)
117117 def canDecompose (typ : Typ )(using Context ): Boolean = typ.canDecompose
118118 def decompose (typ : Typ )(using Context ): List [Typ ] = typ.decompose
119+ def nullSpace (using Context ): Space = Typ (ConstantType (Constant (null )), decomposed = false )
119120
120121 /** Simplify space such that a space equal to `Empty` becomes `Empty` */
121122 def computeSimplify (space : Space )(using Context ): Space = trace(i " simplify( $space) " )(space match {
@@ -336,6 +337,13 @@ object SpaceEngine {
336337 case pat : Ident if isBackquoted(pat) =>
337338 Typ (pat.tpe, decomposed = false )
338339
340+ case Ident (nme.WILDCARD ) =>
341+ val tp = pat.tpe.stripAnnots.widenSkolem
342+ val isNullable = tp.isInstanceOf [FlexibleType ] || tp.classSymbol.isNullableClass
343+ val tpSpace = Typ (erase(tp, isValue = true ), decomposed = false )
344+ if isNullable then Or (tpSpace :: nullSpace :: Nil )
345+ else tpSpace
346+
339347 case Ident (_) | Select (_, _) =>
340348 Typ (erase(pat.tpe.stripAnnots.widenSkolem, isValue = true ), decomposed = false )
341349
@@ -667,7 +675,7 @@ object SpaceEngine {
667675 case tp => (tp, Nil )
668676 val (tp, typeArgs) = getAppliedClass(tpOriginal)
669677 // This function is needed to get the arguments of the types that will be applied to the class.
670- // This is necessary because if the arguments of the types contain Nothing,
678+ // This is necessary because if the arguments of the types contain Nothing,
671679 // then this can affect whether the class will be taken into account during the exhaustiveness check
672680 def getTypeArgs (parent : Symbol , child : Symbol , typeArgs : List [Type ]): List [Type ] =
673681 val superType = child.typeRef.superType
@@ -930,7 +938,7 @@ object SpaceEngine {
930938 if isNullable && ! ctx.mode.is(Mode .SafeNulls )
931939 then project(OrType (selTyp, ConstantType (Constant (null )), soft = false ))
932940 else project(selTyp)
933-
941+ var hadNullOnly = false
934942 @ tailrec def recur (cases : List [CaseDef ], prevs : List [Space ], deferred : List [Tree ]): Unit =
935943 cases match
936944 case Nil =>
@@ -946,11 +954,19 @@ object SpaceEngine {
946954
947955 if pat != EmptyTree // rethrow case of catch uses EmptyTree
948956 && ! pat.symbol.isAllOf(SyntheticCase , butNot= Method ) // ExpandSAMs default cases use SyntheticCase
949- && isSubspace(covered, prev)
950957 then
951- val nullOnly = isNullable && rest.isEmpty && isWildcardArg(pat)
952- val msg = if nullOnly then MatchCaseOnlyNullWarning () else MatchCaseUnreachable ()
953- report.warning(msg, pat.srcPos)
958+ if isSubspace(covered, prev) then
959+ report.warning(MatchCaseUnreachable (), pat.srcPos)
960+ else if isNullable && ! hadNullOnly && isWildcardArg(pat)
961+ && isSubspace(covered, Or (prev :: nullSpace :: Nil )) then
962+ // Issue OnlyNull warning only if:
963+ // 1. The target space is nullable;
964+ // 2. OnlyNull warning has not been issued before;
965+ // 3. The pattern is a wildcard pattern;
966+ // 4. The pattern is not covered by the previous cases,
967+ // but covered by the previous cases with null.
968+ hadNullOnly = true
969+ report.warning(MatchCaseOnlyNullWarning (), pat.srcPos)
954970
955971 // in redundancy check, take guard as false in order to soundly approximate
956972 val newPrev = if guard.isEmpty then covered :: prevs else prevs
0 commit comments