// Copyright 2012 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use hir::def::Def; use hir::def_id::DefId; use 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.node { 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.def { Def::Variant(..) | Def::VariantCtor(..) => true, _ => false } } PatKind::Slice(..) => true, _ => false } } pub fn is_const(&self) -> bool { match self.node { PatKind::Path(hir::QPath::TypeRelative(..)) => true, PatKind::Path(hir::QPath::Resolved(_, ref path)) => { match path.def { Def::Const(..) | Def::AssociatedConst(..) => true, _ => false } } _ => 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: F) where F: FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident), { self.walk(|p| { if let PatKind::Binding(binding_mode, _, ident, _) = p.node { f(binding_mode, p.hir_id, p.span, ident); } 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 { let mut contains_bindings = false; self.walk(|p| { if let PatKind::Binding(..) = p.node { contains_bindings = true; false // there's at least one binding, can short circuit now. } else { true } }); contains_bindings } /// 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 { let mut contains_bindings = false; self.walk(|p| { match p.node { PatKind::Binding(..) | PatKind::Wild => { contains_bindings = true; false // there's at least one binding/wildcard, can short circuit now. } _ => true } }); contains_bindings } pub fn simple_ident(&self) -> Option { match self.node { PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) | PatKind::Binding(hir::BindingAnnotation::Mutable, _, ident, None) => Some(ident), _ => None, } } /// Return 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.node { PatKind::Path(hir::QPath::Resolved(_, ref path)) | PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..) | PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => { match path.def { Def::Variant(id) | Def::VariantCtor(id, ..) => variants.push(id), _ => () } } _ => () } 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 #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 } } impl hir::Arm { /// Checks if the patterns for this arm contain any `ref` or `ref mut` /// bindings, and if yes whether its containing mutable ones or just immutables ones. pub fn contains_explicit_ref_binding(&self) -> Option { // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed // for #42640 (default match binding modes). // // See #44848. self.pats.iter() .filter_map(|pat| pat.contains_explicit_ref_binding()) .max_by_key(|m| match *m { hir::MutMutable => 1, hir::MutImmutable => 0, }) } }