diff --git a/src/libcore/ascii.rs b/src/libcore/ascii.rs index 4087333e2cf6..0492fbd55faf 100644 --- a/src/libcore/ascii.rs +++ b/src/libcore/ascii.rs @@ -100,6 +100,8 @@ pub fn escape_default(c: u8) -> EscapeDefault { b'\\' => ([b'\\', b'\\', 0, 0], 2), b'\'' => ([b'\\', b'\'', 0, 0], 2), b'"' => ([b'\\', b'"', 0, 0], 2), + // The three arms above are in the following range + #[allow(unreachable_patterns)] b'\x20' ..= b'\x7e' => ([c, 0, 0, 0], 1), _ => ([b'\\', b'x', hexify(c >> 4), hexify(c & 0xf)], 4), }; diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 3ea580528772..542f0ba5c9ca 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -167,13 +167,14 @@ use super::{FieldPat, Pat, PatKind, PatRange}; use super::{PatternFoldable, PatternFolder, compare_const_vals}; use rustc::hir::def_id::DefId; -use rustc::hir::RangeEnd; +use rustc::hir::{RangeEnd, HirId}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Const}; use rustc::ty::layout::{Integer, IntegerExt, VariantIdx, Size}; use rustc::mir::Field; use rustc::mir::interpret::{ConstValue, Scalar, truncate, AllocId, Pointer}; use rustc::util::common::ErrorReported; +use rustc::lint; use syntax::attr::{SignedInt, UnsignedInt}; use syntax_pos::{Span, DUMMY_SP}; @@ -418,7 +419,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] enum Constructor<'tcx> { /// The constructor of all patterns that don't vary by constructor, /// e.g., struct patterns and fixed-length arrays. @@ -426,13 +427,30 @@ enum Constructor<'tcx> { /// Enum variants. Variant(DefId), /// Literal values. - ConstantValue(&'tcx ty::Const<'tcx>), + ConstantValue(&'tcx ty::Const<'tcx>, Span), /// Ranges of literal values (`2..=5` and `2..5`). - ConstantRange(u128, u128, Ty<'tcx>, RangeEnd), + ConstantRange(u128, u128, Ty<'tcx>, RangeEnd, Span), /// Array patterns of length n. Slice(u64), } +// Ignore spans when comparing, they don't carry semantic information as they are only for lints. +impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Constructor::Single, Constructor::Single) => true, + (Constructor::Variant(a), Constructor::Variant(b)) => a == b, + (Constructor::ConstantValue(a, _), Constructor::ConstantValue(b, _)) => a == b, + ( + Constructor::ConstantRange(a_start, a_end, a_ty, a_range_end, _), + Constructor::ConstantRange(b_start, b_end, b_ty, b_range_end, _), + ) => a_start == b_start && a_end == b_end && a_ty == b_ty && a_range_end == b_range_end, + (Constructor::Slice(a), Constructor::Slice(b)) => a == b, + _ => false, + } + } +} + impl<'tcx> Constructor<'tcx> { fn is_slice(&self) -> bool { match self { @@ -447,15 +465,36 @@ impl<'tcx> Constructor<'tcx> { adt: &'tcx ty::AdtDef, ) -> VariantIdx { match self { - &Variant(id) => adt.variant_index_with_id(id), - &Single => { + Variant(id) => adt.variant_index_with_id(*id), + Single => { assert!(!adt.is_enum()); VariantIdx::new(0) } - &ConstantValue(c) => crate::const_eval::const_variant_index(cx.tcx, cx.param_env, c), + ConstantValue(c, _) => crate::const_eval::const_variant_index(cx.tcx, cx.param_env, c), _ => bug!("bad constructor {:?} for adt {:?}", self, adt) } } + + fn display(&self, tcx: TyCtxt<'tcx>) -> String { + match self { + Constructor::ConstantValue(val, _) => format!("{}", val), + Constructor::ConstantRange(lo, hi, ty, range_end, _) => { + // Get the right sign on the output: + let ty = ty::ParamEnv::empty().and(*ty); + format!( + "{}..{}{}", + ty::Const::from_bits(tcx, *lo, ty), + match range_end { + RangeEnd::Included => "=", + RangeEnd::Excluded => "", + }, + ty::Const::from_bits(tcx, *hi, ty), + ) + } + Constructor::Slice(val) => format!("[{}]", val), + _ => bug!("bad constructor being displayed: `{:?}", self), + } + } } #[derive(Clone, Debug)] @@ -484,6 +523,7 @@ pub enum WitnessPreference { struct PatCtxt<'tcx> { ty: Ty<'tcx>, max_slice_length: u64, + span: Span, } /// A witness of non-exhaustiveness for error reporting, represented @@ -610,8 +650,8 @@ impl<'tcx> Witness<'tcx> { _ => { match *ctor { - ConstantValue(value) => PatKind::Constant { value }, - ConstantRange(lo, hi, ty, end) => PatKind::Range(PatRange { + ConstantValue(value, _) => PatKind::Constant { value }, + ConstantRange(lo, hi, ty, end, _) => PatKind::Range(PatRange { lo: ty::Const::from_bits(cx.tcx, lo, ty::ParamEnv::empty().and(ty)), hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)), end, @@ -647,7 +687,7 @@ fn all_constructors<'a, 'tcx>( let ctors = match pcx.ty.kind { ty::Bool => { [true, false].iter().map(|&b| { - ConstantValue(ty::Const::from_bool(cx.tcx, b)) + ConstantValue(ty::Const::from_bool(cx.tcx, b), pcx.span) }).collect() } ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { @@ -679,15 +719,19 @@ fn all_constructors<'a, 'tcx>( ty::Char => { vec![ // The valid Unicode Scalar Value ranges. - ConstantRange('\u{0000}' as u128, - '\u{D7FF}' as u128, - cx.tcx.types.char, - RangeEnd::Included + ConstantRange( + '\u{0000}' as u128, + '\u{D7FF}' as u128, + cx.tcx.types.char, + RangeEnd::Included, + pcx.span, ), - ConstantRange('\u{E000}' as u128, - '\u{10FFFF}' as u128, - cx.tcx.types.char, - RangeEnd::Included + ConstantRange( + '\u{E000}' as u128, + '\u{10FFFF}' as u128, + cx.tcx.types.char, + RangeEnd::Included, + pcx.span, ), ] } @@ -695,12 +739,12 @@ fn all_constructors<'a, 'tcx>( let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; let min = 1u128 << (bits - 1); let max = min - 1; - vec![ConstantRange(min, max, pcx.ty, RangeEnd::Included)] + vec![ConstantRange(min, max, pcx.ty, RangeEnd::Included, pcx.span)] } ty::Uint(uty) => { let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); let max = truncate(u128::max_value(), size); - vec![ConstantRange(0, max, pcx.ty, RangeEnd::Included)] + vec![ConstantRange(0, max, pcx.ty, RangeEnd::Included, pcx.span)] } _ => { if cx.is_uninhabited(pcx.ty) { @@ -827,10 +871,11 @@ where /// /// `IntRange` is never used to encode an empty range or a "range" that wraps /// around the (offset) space: i.e., `range.lo <= range.hi`. -#[derive(Clone)] +#[derive(Clone, Debug)] struct IntRange<'tcx> { pub range: RangeInclusive, pub ty: Ty<'tcx>, + pub span: Span, } impl<'tcx> IntRange<'tcx> { @@ -860,6 +905,7 @@ impl<'tcx> IntRange<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, value: &Const<'tcx>, + span: Span, ) -> Option> { if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) { let ty = value.ty; @@ -877,7 +923,7 @@ impl<'tcx> IntRange<'tcx> { return None }; let val = val ^ bias; - Some(IntRange { range: val..=val, ty }) + Some(IntRange { range: val..=val, ty, span }) } else { None } @@ -890,6 +936,7 @@ impl<'tcx> IntRange<'tcx> { hi: u128, ty: Ty<'tcx>, end: &RangeEnd, + span: Span, ) -> Option> { if Self::is_integral(ty) { // Perform a shift if the underlying types are signed, @@ -901,7 +948,7 @@ impl<'tcx> IntRange<'tcx> { None } else { let offset = (*end == RangeEnd::Excluded) as u128; - Some(IntRange { range: lo..=(hi - offset), ty }) + Some(IntRange { range: lo..=(hi - offset), ty, span }) } } else { None @@ -916,8 +963,8 @@ impl<'tcx> IntRange<'tcx> { // Floating-point ranges are permitted and we don't want // to consider them when constructing integer ranges. match ctor { - ConstantRange(lo, hi, ty, end) => Self::from_range(tcx, *lo, *hi, ty, end), - ConstantValue(val) => Self::from_const(tcx, param_env, val), + ConstantRange(lo, hi, ty, end, span) => Self::from_range(tcx, *lo, *hi, ty, end, *span), + ConstantValue(val, span) => Self::from_const(tcx, param_env, val, *span), _ => None, } } @@ -930,7 +977,7 @@ impl<'tcx> IntRange<'tcx> { loop { match pat.kind { box PatKind::Constant { value } => { - return Self::from_const(tcx, param_env, value); + return Self::from_const(tcx, param_env, value, pat.span); } box PatKind::Range(PatRange { lo, hi, end }) => { return Self::from_range( @@ -939,6 +986,7 @@ impl<'tcx> IntRange<'tcx> { hi.eval_bits(tcx, param_env, hi.ty), &lo.ty, &end, + pat.span, ); } box PatKind::AscribeUserType { ref subpattern, .. } => { @@ -965,14 +1013,15 @@ impl<'tcx> IntRange<'tcx> { tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, r: RangeInclusive, + span: Span, ) -> Constructor<'tcx> { let bias = IntRange::signed_bias(tcx, ty); let (lo, hi) = r.into_inner(); if lo == hi { let ty = ty::ParamEnv::empty().and(ty); - ConstantValue(ty::Const::from_bits(tcx, lo ^ bias, ty)) + ConstantValue(ty::Const::from_bits(tcx, lo ^ bias, ty), span) } else { - ConstantRange(lo ^ bias, hi ^ bias, ty, RangeEnd::Included) + ConstantRange(lo ^ bias, hi ^ bias, ty, RangeEnd::Included, span) } } @@ -995,17 +1044,23 @@ impl<'tcx> IntRange<'tcx> { if lo > subrange_hi || subrange_lo > hi { // The pattern doesn't intersect with the subrange at all, // so the subrange remains untouched. - remaining_ranges.push(Self::range_to_ctor(tcx, ty, subrange_lo..=subrange_hi)); + remaining_ranges.push( + Self::range_to_ctor(tcx, ty, subrange_lo..=subrange_hi, self.span), + ); } else { if lo > subrange_lo { // The pattern intersects an upper section of the // subrange, so a lower section will remain. - remaining_ranges.push(Self::range_to_ctor(tcx, ty, subrange_lo..=(lo - 1))); + remaining_ranges.push( + Self::range_to_ctor(tcx, ty, subrange_lo..=(lo - 1), self.span), + ); } if hi < subrange_hi { // The pattern intersects a lower section of the // subrange, so an upper section will remain. - remaining_ranges.push(Self::range_to_ctor(tcx, ty, (hi + 1)..=subrange_hi)); + remaining_ranges.push( + Self::range_to_ctor(tcx, ty, (hi + 1)..=subrange_hi, self.span), + ); } } } @@ -1017,7 +1072,8 @@ impl<'tcx> IntRange<'tcx> { let (lo, hi) = (*self.range.start(), *self.range.end()); let (other_lo, other_hi) = (*other.range.start(), *other.range.end()); if lo <= other_hi && other_lo <= hi { - Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty }) + let span = other.span; + Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty, span }) } else { None } @@ -1127,6 +1183,7 @@ pub fn is_useful<'p, 'a, 'tcx>( matrix: &Matrix<'p, 'tcx>, v: &[&Pat<'tcx>], witness: WitnessPreference, + hir_id: HirId, ) -> Usefulness<'tcx> { let &Matrix(ref rows) = matrix; debug!("is_useful({:#?}, {:#?})", matrix, v); @@ -1149,6 +1206,10 @@ pub fn is_useful<'p, 'a, 'tcx>( assert!(rows.iter().all(|r| r.len() == v.len())); + let (ty, span) = rows.iter() + .map(|r| (r[0].ty, r[0].span)) + .find(|(ty, _)| !ty.references_error()) + .unwrap_or((v[0].ty, v[0].span)); let pcx = PatCtxt { // TyErr is used to represent the type of wildcard patterns matching // against inaccessible (private) fields of structs, so that we won't @@ -1169,8 +1230,9 @@ pub fn is_useful<'p, 'a, 'tcx>( // FIXME: this might lead to "unstable" behavior with macro hygiene // introducing uninhabited patterns for inaccessible fields. We // need to figure out how to model that. - ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error()).unwrap_or(v[0].ty), - max_slice_length: max_slice_length(cx, rows.iter().map(|r| r[0]).chain(Some(v[0]))) + ty, + max_slice_length: max_slice_length(cx, rows.iter().map(|r| r[0]).chain(Some(v[0]))), + span, }; debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]); @@ -1184,9 +1246,9 @@ pub fn is_useful<'p, 'a, 'tcx>( Useful } else { split_grouped_constructors( - cx.tcx, cx.param_env, constructors, matrix, pcx.ty, + cx.tcx, cx.param_env, constructors, matrix, pcx.ty, pcx.span, Some(hir_id), ).into_iter().map(|c| - is_useful_specialized(cx, matrix, v, c, pcx.ty, witness) + is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id) ).find(|result| result.is_useful()).unwrap_or(NotUseful) } } else { @@ -1239,8 +1301,11 @@ pub fn is_useful<'p, 'a, 'tcx>( (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching); if cheap_missing_ctors == MissingCtors::Empty && !is_non_exhaustive { - split_grouped_constructors(cx.tcx, cx.param_env, all_ctors, matrix, pcx.ty) - .into_iter().map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)) + split_grouped_constructors( + cx.tcx, cx.param_env, all_ctors, matrix, pcx.ty, DUMMY_SP, None, + ) + .into_iter() + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)) .find(|result| result.is_useful()) .unwrap_or(NotUseful) } else { @@ -1251,7 +1316,7 @@ pub fn is_useful<'p, 'a, 'tcx>( None } }).collect(); - match is_useful(cx, &matrix, &v[1..], witness) { + match is_useful(cx, &matrix, &v[1..], witness, hir_id) { UsefulWithWitness(pats) => { let cx = &*cx; // In this case, there's at least one "free" @@ -1344,6 +1409,7 @@ fn is_useful_specialized<'p, 'a, 'tcx>( ctor: Constructor<'tcx>, lty: Ty<'tcx>, witness: WitnessPreference, + hir_id: HirId, ) -> Usefulness<'tcx> { debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty); @@ -1361,7 +1427,7 @@ fn is_useful_specialized<'p, 'a, 'tcx>( .collect() ); match specialize(cx, v, &ctor, &wild_patterns) { - Some(v) => match is_useful(cx, &matrix, &v, witness) { + Some(v) => match is_useful(cx, &matrix, &v, witness, hir_id) { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses.into_iter() .map(|witness| witness.apply_constructor(cx, &ctor, lty)) @@ -1381,11 +1447,11 @@ fn is_useful_specialized<'p, 'a, 'tcx>( /// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on. /// /// Returns `None` in case of a catch-all, which can't be specialized. -fn pat_constructors<'tcx>(cx: &mut MatchCheckCtxt<'_, 'tcx>, - pat: &Pat<'tcx>, - pcx: PatCtxt<'tcx>) - -> Option>> -{ +fn pat_constructors<'tcx>( + cx: &mut MatchCheckCtxt<'_, 'tcx>, + pat: &Pat<'tcx>, + pcx: PatCtxt<'tcx>, +) -> Option>> { match *pat.kind { PatKind::AscribeUserType { ref subpattern, .. } => pat_constructors(cx, subpattern, pcx), @@ -1394,13 +1460,14 @@ fn pat_constructors<'tcx>(cx: &mut MatchCheckCtxt<'_, 'tcx>, PatKind::Variant { adt_def, variant_index, .. } => { Some(vec![Variant(adt_def.variants[variant_index].def_id)]) } - PatKind::Constant { value } => Some(vec![ConstantValue(value)]), + PatKind::Constant { value } => Some(vec![ConstantValue(value, pat.span)]), PatKind::Range(PatRange { lo, hi, end }) => Some(vec![ConstantRange( lo.eval_bits(cx.tcx, cx.param_env, lo.ty), hi.eval_bits(cx.tcx, cx.param_env, hi.ty), lo.ty, end, + pat.span, )]), PatKind::Array { .. } => match pcx.ty.kind { ty::Array(_, length) => Some(vec![ @@ -1433,7 +1500,7 @@ fn constructor_arity(cx: &MatchCheckCtxt<'a, 'tcx>, ctor: &Constructor<'tcx>, ty ty::Tuple(ref fs) => fs.len() as u64, ty::Slice(..) | ty::Array(..) => match *ctor { Slice(length) => length, - ConstantValue(_) => 0, + ConstantValue(..) => 0, _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) } ty::Ref(..) => 1, @@ -1458,7 +1525,7 @@ fn constructor_sub_pattern_tys<'a, 'tcx>( ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), ty::Slice(ty) | ty::Array(ty, _) => match *ctor { Slice(length) => (0..length).map(|_| ty).collect(), - ConstantValue(_) => vec![], + ConstantValue(..) => vec![], _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) } ty::Ref(_, rty, _) => vec![rty], @@ -1556,8 +1623,8 @@ fn slice_pat_covered_by_const<'tcx>( // constructor is a range or constant with an integer type. fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) -> bool { let ty = match ctor { - ConstantValue(value) => value.ty, - ConstantRange(_, _, ty, _) => ty, + ConstantValue(value, _) => value.ty, + ConstantRange(_, _, ty, _, _) => ty, _ => return false, }; if let ty::Char | ty::Int(_) | ty::Uint(_) = ty.kind { @@ -1599,12 +1666,17 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) /// boundaries for each interval range, sort them, then create constructors for each new interval /// between every pair of boundary points. (This essentially sums up to performing the intuitive /// merging operation depicted above.) +/// +/// `hir_id` is `None` when we're evaluating the wildcard pattern, do not lint for overlapping in +/// ranges that case. fn split_grouped_constructors<'p, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ctors: Vec>, &Matrix(ref m): &Matrix<'p, 'tcx>, ty: Ty<'tcx>, + span: Span, + hir_id: Option, ) -> Vec> { let mut split_ctors = Vec::with_capacity(ctors.len()); @@ -1621,7 +1693,7 @@ fn split_grouped_constructors<'p, 'tcx>( /// Represents a border between 2 integers. Because the intervals spanning borders /// must be able to cover every integer, we need to be able to represent /// 2^128 + 1 such borders. - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] enum Border { JustBefore(u128), AfterMax, @@ -1638,16 +1710,54 @@ fn split_grouped_constructors<'p, 'tcx>( vec![from, to].into_iter() } + // Collect the span and range of all the intersecting ranges to lint on likely + // incorrect range patterns. (#63987) + let mut overlaps = vec![]; // `borders` is the set of borders between equivalence classes: each equivalence // class lies between 2 borders. let row_borders = m.iter() - .flat_map(|row| IntRange::from_pat(tcx, param_env, row[0])) - .flat_map(|range| ctor_range.intersection(&range)) + .flat_map(|row| { + IntRange::from_pat(tcx, param_env, row[0]).map(|r| (r, row.len())) + }) + .flat_map(|(range, row_len)| { + let intersection = ctor_range.intersection(&range); + if let (Some(range), 1) = (&intersection, row_len) { + // FIXME: for now, only check for overlapping ranges on simple range + // patterns. Otherwise with the current logic the following is detected + // as overlapping: + // match (10u8, true) { + // (0 ..= 125, false) => {} + // (126 ..= 255, false) => {} + // (0 ..= 255, true) => {} + // } + overlaps.push(range.clone()); + } + intersection + }) .flat_map(|range| range_borders(range)); let ctor_borders = range_borders(ctor_range.clone()); let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); borders.sort_unstable(); + if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { + let mut err = tcx.struct_span_lint_hir( + lint::builtin::UNREACHABLE_PATTERNS, + hir_id, + ctor_range.span, + "multiple patterns covering the same range", + ); + err.span_label(ctor_range.span, "overlapping patterns"); + for int_range in overlaps { + // Use the real type for user display of the ranges: + err.span_label(int_range.span, &format!( + "this range overlaps on `{}`", + IntRange::range_to_ctor(tcx, ty, int_range.range, DUMMY_SP) + .display(tcx), + )); + } + err.emit(); + } + // We're going to iterate through every pair of borders, making sure that each // represents an interval of nonnegative length, and convert each such interval // into a constructor. @@ -1655,18 +1765,18 @@ fn split_grouped_constructors<'p, 'tcx>( match (window[0], window[1]) { (Border::JustBefore(n), Border::JustBefore(m)) => { if n < m { - Some(IntRange { range: n..=(m - 1), ty }) + Some(IntRange { range: n..=(m - 1), ty, span }) } else { None } } (Border::JustBefore(n), Border::AfterMax) => { - Some(IntRange { range: n..=u128::MAX, ty }) + Some(IntRange { range: n..=u128::MAX, ty, span }) } (Border::AfterMax, _) => None, } }) { - split_ctors.push(IntRange::range_to_ctor(tcx, ty, range)); + split_ctors.push(IntRange::range_to_ctor(tcx, ty, range, span)); } } // Any other constructor can be used unchanged. @@ -1701,13 +1811,13 @@ fn constructor_covered_by_range<'tcx>( }; } match *ctor { - ConstantValue(value) => { + ConstantValue(value, _) => { let to = some_or_ok!(cmp_to(value)); let end = (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); Ok(some_or_ok!(cmp_from(value)) && end) }, - ConstantRange(from, to, ty, RangeEnd::Included) => { + ConstantRange(from, to, ty, RangeEnd::Included, _) => { let to = some_or_ok!(cmp_to(ty::Const::from_bits( tcx, to, @@ -1721,7 +1831,7 @@ fn constructor_covered_by_range<'tcx>( ty::ParamEnv::empty().and(ty), ))) && end) }, - ConstantRange(from, to, ty, RangeEnd::Excluded) => { + ConstantRange(from, to, ty, RangeEnd::Excluded, _) => { let to = some_or_ok!(cmp_to(ty::Const::from_bits( tcx, to, @@ -1915,7 +2025,7 @@ fn specialize<'p, 'a: 'p, 'tcx>( None } } - ConstantValue(cv) => { + ConstantValue(cv, _) => { match slice_pat_covered_by_const( cx.tcx, pat.span, cv, prefix, slice, suffix, cx.param_env, ) { diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 9bed4fb66ea9..fe57e12bcfcc 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -10,6 +10,7 @@ use rustc::ty::subst::{InternalSubsts, SubstsRef}; use rustc::lint; use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc::hir::HirId; use rustc::hir::def::*; use rustc::hir::def_id::DefId; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; @@ -239,7 +240,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { .map(|pat| smallvec![pat.0]) .collect(); let scrut_ty = self.tables.node_type(scrut.hir_id); - check_exhaustive(cx, scrut_ty, scrut.span, &matrix); + check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id); }) } @@ -256,7 +257,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { expand_pattern(cx, pattern) ]].into_iter().collect(); - let witnesses = match check_not_useful(cx, pattern_ty, &pats) { + let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) { Ok(_) => return, Err(err) => err, }; @@ -389,7 +390,7 @@ fn check_arms<'tcx>( for &(pat, hir_pat) in pats { let v = smallvec![pat]; - match is_useful(cx, &seen, &v, LeaveOutWitness) { + match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) { NotUseful => { match source { hir::MatchSource::IfDesugar { .. } | @@ -428,6 +429,13 @@ fn check_arms<'tcx>( hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { + match pat.kind { + box PatternKind::Range(..) => { + // Covered in `is_useful() with more context` + break; + } + _ => {} + } let mut err = cx.tcx.struct_span_lint_hir( lint::builtin::UNREACHABLE_PATTERNS, hir_pat.hir_id, @@ -465,9 +473,10 @@ fn check_not_useful( cx: &mut MatchCheckCtxt<'_, 'tcx>, ty: Ty<'tcx>, matrix: &Matrix<'_, 'tcx>, + hir_id: HirId, ) -> Result<(), Vec>> { let wild_pattern = super::Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }; - match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) { + match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness, hir_id) { NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. UsefulWithWitness(pats) => Err(if pats.is_empty() { vec![wild_pattern] @@ -483,8 +492,9 @@ fn check_exhaustive<'tcx>( scrut_ty: Ty<'tcx>, sp: Span, matrix: &Matrix<'_, 'tcx>, + hir_id: HirId, ) { - let witnesses = match check_not_useful(cx, scrut_ty, matrix) { + let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) { Ok(_) => return, Err(err) => err, }; diff --git a/src/test/ui/check_match/issue-43253.rs b/src/test/ui/check_match/issue-43253.rs index a4d6e9b777f0..548300da0340 100644 --- a/src/test/ui/check_match/issue-43253.rs +++ b/src/test/ui/check_match/issue-43253.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass #![feature(exclusive_range_pattern)] #![warn(unreachable_patterns)] @@ -13,7 +13,7 @@ fn main() { match 10 { 1..10 => {}, - 9..=10 => {}, + 9..=10 => {}, //~ WARNING multiple patterns covering the same range _ => {}, } @@ -23,22 +23,25 @@ fn main() { _ => {}, } - // These cases should generate an "unreachable pattern" warning. + // These cases should generate "unreachable pattern" warnings. match 10 { 1..10 => {}, - 9 => {}, + 9 => {}, //~ WARNING unreachable pattern _ => {}, } match 10 { 1..10 => {}, - 8..=9 => {}, + 8..=9 => {}, //~ WARNING multiple patterns covering the same range _ => {}, } match 10 { - 1..10 => {}, - 9..=9 => {}, + 5..7 => {}, + 6 => {}, //~ WARNING unreachable pattern + 1..10 => {}, //~ WARNING multiple patterns covering the same range + 9..=9 => {}, //~ WARNING unreachable pattern + 6 => {}, //~ WARNING unreachable pattern _ => {}, } } diff --git a/src/test/ui/check_match/issue-43253.stderr b/src/test/ui/check_match/issue-43253.stderr index d961f623e1fa..24dfa52beb68 100644 --- a/src/test/ui/check_match/issue-43253.stderr +++ b/src/test/ui/check_match/issue-43253.stderr @@ -1,8 +1,10 @@ -warning: unreachable pattern - --> $DIR/issue-43253.rs:29:9 +warning: multiple patterns covering the same range + --> $DIR/issue-43253.rs:16:9 | -LL | 9 => {}, - | ^ +LL | 1..10 => {}, + | ----- this range overlaps on `9i32` +LL | 9..=10 => {}, + | ^^^^^^ overlapping patterns | note: lint level defined here --> $DIR/issue-43253.rs:4:9 @@ -11,14 +13,44 @@ LL | #![warn(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ warning: unreachable pattern + --> $DIR/issue-43253.rs:29:9 + | +LL | 9 => {}, + | ^ + +warning: multiple patterns covering the same range --> $DIR/issue-43253.rs:35:9 | +LL | 1..10 => {}, + | ----- this range overlaps on `8i32..=9i32` LL | 8..=9 => {}, - | ^^^^^ + | ^^^^^ overlapping patterns warning: unreachable pattern --> $DIR/issue-43253.rs:41:9 | +LL | 6 => {}, + | ^ + +warning: multiple patterns covering the same range + --> $DIR/issue-43253.rs:42:9 + | +LL | 5..7 => {}, + | ---- this range overlaps on `5i32..=6i32` +LL | 6 => {}, + | - this range overlaps on `6i32` +LL | 1..10 => {}, + | ^^^^^ overlapping patterns + +warning: unreachable pattern + --> $DIR/issue-43253.rs:43:9 + | LL | 9..=9 => {}, | ^^^^^ +warning: unreachable pattern + --> $DIR/issue-43253.rs:44:9 + | +LL | 6 => {}, + | ^ + diff --git a/src/test/ui/exhaustive_integer_patterns.rs b/src/test/ui/exhaustive_integer_patterns.rs index 2570bc8a5607..ce2b44b8ef16 100644 --- a/src/test/ui/exhaustive_integer_patterns.rs +++ b/src/test/ui/exhaustive_integer_patterns.rs @@ -19,7 +19,7 @@ fn main() { 0 ..= 32 => {} 33 => {} 34 .. 128 => {} - 100 ..= 200 => {} + 100 ..= 200 => {} //~ ERROR multiple patterns covering the same range 200 => {} //~ ERROR unreachable pattern 201 ..= 255 => {} } @@ -41,7 +41,7 @@ fn main() { match x { //~ ERROR non-exhaustive patterns -7 => {} -5..=120 => {} - -2..=20 => {} //~ ERROR unreachable pattern + -2..=20 => {} //~ ERROR multiple patterns covering the same range 125 => {} } @@ -135,9 +135,9 @@ fn main() { (125 .. 128, false) => {} } - match 0u8 { // ok + match 0u8 { 0 .. 2 => {} - 1 ..= 2 => {} + 1 ..= 2 => {} //~ ERROR multiple patterns covering the same range _ => {} } diff --git a/src/test/ui/exhaustive_integer_patterns.stderr b/src/test/ui/exhaustive_integer_patterns.stderr index 6c4b7b0cc035..ef3faceaa7e1 100644 --- a/src/test/ui/exhaustive_integer_patterns.stderr +++ b/src/test/ui/exhaustive_integer_patterns.stderr @@ -1,8 +1,10 @@ -error: unreachable pattern - --> $DIR/exhaustive_integer_patterns.rs:23:9 +error: multiple patterns covering the same range + --> $DIR/exhaustive_integer_patterns.rs:22:9 | -LL | 200 => {} - | ^^^ +LL | 34 .. 128 => {} + | --------- this range overlaps on `100u8..=127u8` +LL | 100 ..= 200 => {} + | ^^^^^^^^^^^ overlapping patterns | note: lint level defined here --> $DIR/exhaustive_integer_patterns.rs:4:9 @@ -10,6 +12,12 @@ note: lint level defined here LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ +error: unreachable pattern + --> $DIR/exhaustive_integer_patterns.rs:23:9 + | +LL | 200 => {} + | ^^^ + error[E0004]: non-exhaustive patterns: `128u8..=std::u8::MAX` not covered --> $DIR/exhaustive_integer_patterns.rs:28:11 | @@ -26,11 +34,13 @@ LL | match x { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: unreachable pattern +error: multiple patterns covering the same range --> $DIR/exhaustive_integer_patterns.rs:44:9 | +LL | -5..=120 => {} + | -------- this range overlaps on `-2i8..=20i8` LL | -2..=20 => {} - | ^^^^^^^ + | ^^^^^^^ overlapping patterns error[E0004]: non-exhaustive patterns: `std::i8::MIN..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered --> $DIR/exhaustive_integer_patterns.rs:41:11 @@ -80,6 +90,14 @@ LL | match (0u8, true) { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms +error: multiple patterns covering the same range + --> $DIR/exhaustive_integer_patterns.rs:140:9 + | +LL | 0 .. 2 => {} + | ------ this range overlaps on `1u8` +LL | 1 ..= 2 => {} + | ^^^^^^^ overlapping patterns + error[E0004]: non-exhaustive patterns: `std::u128::MAX` not covered --> $DIR/exhaustive_integer_patterns.rs:145:11 | @@ -104,6 +122,6 @@ LL | match 0u128 { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/match/match-range-fail-dominate.rs b/src/test/ui/match/match-range-fail-dominate.rs index a0cc773d20ed..4c0f57ffbeed 100644 --- a/src/test/ui/match/match-range-fail-dominate.rs +++ b/src/test/ui/match/match-range-fail-dominate.rs @@ -1,39 +1,41 @@ -//error-pattern: unreachable -//error-pattern: unreachable -//error-pattern: unreachable -//error-pattern: unreachable -//error-pattern: unreachable - #![deny(unreachable_patterns)] fn main() { match 5 { 1 ..= 10 => { } - 5 ..= 6 => { } + 5 ..= 6 => { } //~ ERROR multiple patterns covering the same range _ => {} }; match 5 { 3 ..= 6 => { } - 4 ..= 6 => { } + 4 ..= 6 => { } //~ ERROR multiple patterns covering the same range _ => {} }; match 5 { 4 ..= 6 => { } - 4 ..= 6 => { } + 4 ..= 6 => { } //~ ERROR multiple patterns covering the same range _ => {} }; match 'c' { 'A' ..= 'z' => {} - 'a' ..= 'z' => {} + 'a' ..= 'z' => {} //~ ERROR multiple patterns covering the same range _ => {} }; match 1.0f64 { 0.01f64 ..= 6.5f64 => {} - 0.02f64 => {} + //~^ WARNING floating-point types cannot be used in patterns + //~| WARNING floating-point types cannot be used in patterns + //~| WARNING floating-point types cannot be used in patterns + //~| WARNING this was previously accepted by the compiler + //~| WARNING this was previously accepted by the compiler + //~| WARNING this was previously accepted by the compiler + 0.02f64 => {} //~ ERROR unreachable pattern + //~^ WARNING floating-point types cannot be used in patterns + //~| WARNING this was previously accepted by the compiler _ => {} }; } diff --git a/src/test/ui/match/match-range-fail-dominate.stderr b/src/test/ui/match/match-range-fail-dominate.stderr index d0ff4930a451..af667be317de 100644 --- a/src/test/ui/match/match-range-fail-dominate.stderr +++ b/src/test/ui/match/match-range-fail-dominate.stderr @@ -1,35 +1,43 @@ -error: unreachable pattern - --> $DIR/match-range-fail-dominate.rs:12:7 +error: multiple patterns covering the same range + --> $DIR/match-range-fail-dominate.rs:6:7 | +LL | 1 ..= 10 => { } + | -------- this range overlaps on `5i32..=6i32` LL | 5 ..= 6 => { } - | ^^^^^^^ + | ^^^^^^^ overlapping patterns | note: lint level defined here - --> $DIR/match-range-fail-dominate.rs:7:9 + --> $DIR/match-range-fail-dominate.rs:1:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ -error: unreachable pattern +error: multiple patterns covering the same range + --> $DIR/match-range-fail-dominate.rs:12:7 + | +LL | 3 ..= 6 => { } + | ------- this range overlaps on `4i32..=6i32` +LL | 4 ..= 6 => { } + | ^^^^^^^ overlapping patterns + +error: multiple patterns covering the same range --> $DIR/match-range-fail-dominate.rs:18:7 | LL | 4 ..= 6 => { } - | ^^^^^^^ + | ------- this range overlaps on `4i32..=6i32` +LL | 4 ..= 6 => { } + | ^^^^^^^ overlapping patterns -error: unreachable pattern +error: multiple patterns covering the same range --> $DIR/match-range-fail-dominate.rs:24:7 | -LL | 4 ..= 6 => { } - | ^^^^^^^ - -error: unreachable pattern - --> $DIR/match-range-fail-dominate.rs:30:7 - | +LL | 'A' ..= 'z' => {} + | ----------- this range overlaps on `'a'..='z'` LL | 'a' ..= 'z' => {} - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ overlapping patterns warning: floating-point types cannot be used in patterns - --> $DIR/match-range-fail-dominate.rs:35:7 + --> $DIR/match-range-fail-dominate.rs:29:7 | LL | 0.01f64 ..= 6.5f64 => {} | ^^^^^^^ @@ -39,7 +47,7 @@ LL | 0.01f64 ..= 6.5f64 => {} = note: for more information, see issue #41620 warning: floating-point types cannot be used in patterns - --> $DIR/match-range-fail-dominate.rs:35:19 + --> $DIR/match-range-fail-dominate.rs:29:19 | LL | 0.01f64 ..= 6.5f64 => {} | ^^^^^^ @@ -63,7 +71,7 @@ LL | 0.02f64 => {} | ^^^^^^^ warning: floating-point types cannot be used in patterns - --> $DIR/match-range-fail-dominate.rs:35:7 + --> $DIR/match-range-fail-dominate.rs:29:7 | LL | 0.01f64 ..= 6.5f64 => {} | ^^^^^^^ diff --git a/src/test/ui/precise_pointer_size_matching.rs b/src/test/ui/precise_pointer_size_matching.rs index 759b63b188b3..239197af2ca1 100644 --- a/src/test/ui/precise_pointer_size_matching.rs +++ b/src/test/ui/precise_pointer_size_matching.rs @@ -23,11 +23,11 @@ fn main() { match 0isize { //~ ERROR non-exhaustive patterns 1 ..= 8 => {} - -5 ..= 20 => {} + -5 ..= 20 => {} //~ ERROR multiple patterns covering the same range } match 0usize { //~ ERROR non-exhaustive patterns 1 ..= 8 => {} - 5 ..= 20 => {} + 5 ..= 20 => {} //~ ERROR multiple patterns covering the same range } } diff --git a/src/test/ui/precise_pointer_size_matching.stderr b/src/test/ui/precise_pointer_size_matching.stderr index 2c2c2aa04c23..06b0e73c5a3b 100644 --- a/src/test/ui/precise_pointer_size_matching.stderr +++ b/src/test/ui/precise_pointer_size_matching.stderr @@ -1,3 +1,17 @@ +error: multiple patterns covering the same range + --> $DIR/precise_pointer_size_matching.rs:26:9 + | +LL | 1 ..= 8 => {} + | ------- this range overlaps on `1isize..=8isize` +LL | -5 ..= 20 => {} + | ^^^^^^^^^ overlapping patterns + | +note: lint level defined here + --> $DIR/precise_pointer_size_matching.rs:11:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + error[E0004]: non-exhaustive patterns: `std::isize::MIN..=-6isize` and `21isize..=std::isize::MAX` not covered --> $DIR/precise_pointer_size_matching.rs:24:11 | @@ -6,6 +20,14 @@ LL | match 0isize { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms +error: multiple patterns covering the same range + --> $DIR/precise_pointer_size_matching.rs:31:9 + | +LL | 1 ..= 8 => {} + | ------- this range overlaps on `5usize..=8usize` +LL | 5 ..= 20 => {} + | ^^^^^^^^ overlapping patterns + error[E0004]: non-exhaustive patterns: `0usize` and `21usize..=std::usize::MAX` not covered --> $DIR/precise_pointer_size_matching.rs:29:11 | @@ -14,6 +36,6 @@ LL | match 0usize { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0004`.