use crate::hir::def::{CtorOf, Res, DefKind}; use crate::hir::def_id::DefId; use crate::hir::{self, HirId, PatKind}; use syntax::ast; use syntax_pos::Span; use std::iter::{Enumerate, ExactSizeIterator}; pub struct EnumerateAndAdjust { enumerate: Enumerate, gap_pos: usize, gap_len: usize, } impl Iterator for EnumerateAndAdjust where I: Iterator { type Item = (usize, ::Item); fn next(&mut self) -> Option<(usize, ::Item)> { self.enumerate.next().map(|(i, elem)| { (if i < self.gap_pos { i } else { i + self.gap_len }, elem) }) } fn size_hint(&self) -> (usize, Option) { self.enumerate.size_hint() } } pub trait EnumerateAndAdjustIterator { fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option) -> EnumerateAndAdjust where Self: Sized; } impl EnumerateAndAdjustIterator for T { fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option) -> EnumerateAndAdjust where Self: Sized { let actual_len = self.len(); EnumerateAndAdjust { enumerate: self.enumerate(), gap_pos: gap_pos.unwrap_or(expected_len), gap_len: expected_len - actual_len, } } } impl hir::Pat { pub fn is_refutable(&self) -> bool { match self.kind { PatKind::Lit(_) | PatKind::Range(..) | PatKind::Path(hir::QPath::Resolved(Some(..), _)) | PatKind::Path(hir::QPath::TypeRelative(..)) => true, PatKind::Path(hir::QPath::Resolved(_, ref path)) | PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..) | PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => { match path.res { Res::Def(DefKind::Variant, _) => true, _ => false } } PatKind::Slice(..) => true, _ => false } } /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident)) { self.walk(|p| { if let PatKind::Binding(binding_mode, _, ident, _) = p.kind { f(binding_mode, p.hir_id, p.span, ident); } true }); } /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }`. /// /// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited. pub fn each_binding_or_first( &self, f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident), ) { self.walk(|p| match &p.kind { PatKind::Or(ps) => { ps[0].each_binding_or_first(f); false }, PatKind::Binding(bm, _, ident, _) => { f(*bm, p.hir_id, p.span, *ident); true } _ => true, }) } /// Checks if the pattern contains any patterns that bind something to /// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`. pub fn contains_bindings(&self) -> bool { self.satisfies(|p| match p.kind { PatKind::Binding(..) => true, _ => false, }) } /// Checks if the pattern contains any patterns that bind something to /// an ident or wildcard, e.g., `foo`, or `Foo(_)`, `foo @ Bar(..)`, pub fn contains_bindings_or_wild(&self) -> bool { self.satisfies(|p| match p.kind { PatKind::Binding(..) | PatKind::Wild => true, _ => false, }) } /// Checks if the pattern satisfies the given predicate on some sub-pattern. fn satisfies(&self, pred: impl Fn(&Self) -> bool) -> bool { let mut satisfies = false; self.walk_short(|p| { if pred(p) { satisfies = true; false // Found one, can short circuit now. } else { true } }); satisfies } pub fn simple_ident(&self) -> Option { match self.kind { PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) | PatKind::Binding(hir::BindingAnnotation::Mutable, _, ident, None) => Some(ident), _ => None, } } /// Returns variants that are necessary to exist for the pattern to match. pub fn necessary_variants(&self) -> Vec { let mut variants = vec![]; self.walk(|p| match &p.kind { PatKind::Or(_) => false, PatKind::Path(hir::QPath::Resolved(_, path)) | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..) | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => { if let Res::Def(DefKind::Variant, id) | Res::Def(DefKind::Ctor(CtorOf::Variant, ..), id) = path.res { variants.push(id); } true } _ => true, }); variants.sort(); variants.dedup(); variants } /// Checks if the pattern contains any `ref` or `ref mut` bindings, and if /// yes whether it contains mutable or just immutables ones. // // FIXME(tschottdorf): this is problematic as the HIR is being scraped, but // ref bindings are be implicit after #42640 (default match binding modes). See issue #44848. pub fn contains_explicit_ref_binding(&self) -> Option { let mut result = None; self.each_binding(|annotation, _, _, _| { match annotation { hir::BindingAnnotation::Ref => match result { None | Some(hir::MutImmutable) => result = Some(hir::MutImmutable), _ => {} } hir::BindingAnnotation::RefMut => result = Some(hir::MutMutable), _ => {} } }); result } }