@@ -794,26 +794,49 @@ trait Checking {
794
794
795
795
/** Check that pattern `pat` is irrefutable for scrutinee type `sel.tpe`.
796
796
* This means `sel` is either marked @unchecked or `sel.tpe` conforms to the
797
- * pattern's type. If pattern is an UnApply, do the check recursively.
797
+ * pattern's type. If pattern is an UnApply, also check that the extractor is
798
+ * irrefutable, and do the check recursively.
798
799
*/
799
800
def checkIrrefutable (sel : Tree , pat : Tree , isPatDef : Boolean )(using Context ): Boolean = {
800
801
val pt = sel.tpe
801
802
802
- def fail (pat : Tree , pt : Type ): Boolean = {
803
- var reportedPt = pt.dropAnnot(defn.UncheckedAnnot )
804
- if (! pat.tpe.isSingleton) reportedPt = reportedPt.widen
805
- val problem = if (pat.tpe <:< reportedPt) " is more specialized than" else " does not match"
806
- val fix = if (isPatDef) " adding `: @unchecked` after the expression" else " writing `case ` before the full pattern"
807
- val pos = if (isPatDef) sel.srcPos else pat.srcPos
803
+ enum Reason :
804
+ case NonConforming , RefutableExtractor
805
+
806
+ def fail (pat : Tree , pt : Type , reason : Reason ): Boolean = {
807
+ import Reason ._
808
+ val message = reason match
809
+ case NonConforming =>
810
+ var reportedPt = pt.dropAnnot(defn.UncheckedAnnot )
811
+ if ! pat.tpe.isSingleton then reportedPt = reportedPt.widen
812
+ val problem = if pat.tpe <:< reportedPt then " is more specialized than" else " does not match"
813
+ ex " pattern's type ${pat.tpe} $problem the right hand side expression's type $reportedPt"
814
+ case RefutableExtractor =>
815
+ val extractor =
816
+ val UnApply (fn, _, _) = pat : @ unchecked
817
+ fn match
818
+ case Select (id, _) => id
819
+ case TypeApply (Select (id, _), _) => id
820
+ em " pattern binding uses refutable extractor ` $extractor` "
821
+
822
+ val fix = if isPatDef then " adding `: @unchecked` after the expression" else " writing `case ` before the full pattern"
823
+ val usage = reason match
824
+ case NonConforming => " the narrowing"
825
+ case RefutableExtractor => " this usage"
826
+ val pos =
827
+ if isPatDef then reason match
828
+ case NonConforming => sel.srcPos
829
+ case RefutableExtractor => pat.source.atSpan(pat.span union sel.span)
830
+ else pat.srcPos
808
831
report.warning(
809
- ex """ pattern's type ${pat.tpe} $problem the right hand side expression's type $reportedPt
832
+ em """ $message
810
833
|
811
- |If the narrowing is intentional, this can be communicated by $fix. ${err.rewriteNotice}""" ,
834
+ |If $usage is intentional, this can be communicated by $fix. ${err.rewriteNotice}""" ,
812
835
pos)
813
836
false
814
837
}
815
838
816
- def check (pat : Tree , pt : Type ): Boolean = (pt <:< pat.tpe) || fail(pat, pt)
839
+ def check (pat : Tree , pt : Type ): Boolean = (pt <:< pat.tpe) || fail(pat, pt, Reason . NonConforming )
817
840
818
841
def recur (pat : Tree , pt : Type ): Boolean =
819
842
! sourceVersion.isAtLeast(future) || // only for 3.x for now since mitigations work only after this PR
@@ -824,7 +847,7 @@ trait Checking {
824
847
recur(pat1, pt)
825
848
case UnApply (fn, _, pats) =>
826
849
check(pat, pt) &&
827
- (isIrrefutable(fn, pats.length) || fail(pat, pt)) && {
850
+ (isIrrefutable(fn, pats.length) || fail(pat, pt, Reason . RefutableExtractor )) && {
828
851
val argPts = unapplyArgs(fn.tpe.widen.finalResultType, fn, pats, pat.srcPos)
829
852
pats.corresponds(argPts)(recur)
830
853
}
0 commit comments