summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir/src/pat_util.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir/src/pat_util.rs')
-rw-r--r--compiler/rustc_hir/src/pat_util.rs157
1 files changed, 157 insertions, 0 deletions
diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs
new file mode 100644
index 000000000..93112199b
--- /dev/null
+++ b/compiler/rustc_hir/src/pat_util.rs
@@ -0,0 +1,157 @@
+use crate::def::{CtorOf, DefKind, Res};
+use crate::def_id::DefId;
+use crate::hir::{self, HirId, PatKind};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_span::hygiene::DesugaringKind;
+use rustc_span::symbol::Ident;
+use rustc_span::Span;
+
+use std::iter::{Enumerate, ExactSizeIterator};
+
+pub struct EnumerateAndAdjust<I> {
+ enumerate: Enumerate<I>,
+ gap_pos: usize,
+ gap_len: usize,
+}
+
+impl<I> Iterator for EnumerateAndAdjust<I>
+where
+ I: Iterator,
+{
+ type Item = (usize, <I as Iterator>::Item);
+
+ fn next(&mut self) -> Option<(usize, <I as Iterator>::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<usize>) {
+ self.enumerate.size_hint()
+ }
+}
+
+pub trait EnumerateAndAdjustIterator {
+ fn enumerate_and_adjust(
+ self,
+ expected_len: usize,
+ gap_pos: Option<usize>,
+ ) -> EnumerateAndAdjust<Self>
+ where
+ Self: Sized;
+}
+
+impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
+ fn enumerate_and_adjust(
+ self,
+ expected_len: usize,
+ gap_pos: Option<usize>,
+ ) -> EnumerateAndAdjust<Self>
+ 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<'_> {
+ /// 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, Ident)) {
+ self.walk_always(|p| {
+ if let PatKind::Binding(binding_mode, _, ident, _) = p.kind {
+ f(binding_mode, p.hir_id, p.span, ident);
+ }
+ });
+ }
+
+ /// 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, 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,
+ })
+ }
+
+ pub fn simple_ident(&self) -> Option<Ident> {
+ match self.kind {
+ PatKind::Binding(
+ hir::BindingAnnotation::Unannotated | 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<DefId> {
+ 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 | DefKind::Ctor(CtorOf::Variant, ..), id) =
+ path.res
+ {
+ variants.push(id);
+ }
+ true
+ }
+ _ => true,
+ });
+ // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
+ // the bounds
+ let mut duplicates = FxHashSet::default();
+ variants.retain(|def_id| duplicates.insert(*def_id));
+ 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<hir::Mutability> {
+ let mut result = None;
+ self.each_binding(|annotation, _, _, _| match annotation {
+ hir::BindingAnnotation::Ref => match result {
+ None | Some(hir::Mutability::Not) => result = Some(hir::Mutability::Not),
+ _ => {}
+ },
+ hir::BindingAnnotation::RefMut => result = Some(hir::Mutability::Mut),
+ _ => {}
+ });
+ result
+ }
+
+ /// If the pattern is `Some(<pat>)` from a desugared for loop, returns the inner pattern
+ pub fn for_loop_some(&self) -> Option<&Self> {
+ if self.span.desugaring_kind() == Some(DesugaringKind::ForLoop) {
+ if let hir::PatKind::Struct(_, [pat_field], _) = self.kind {
+ return Some(pat_field.pat);
+ }
+ }
+ None
+ }
+}