rustc_hir/
pat_util.rs

1use std::iter::Enumerate;
2
3use rustc_span::{Ident, Span};
4
5use crate::def::{CtorOf, DefKind, Res};
6use crate::def_id::{DefId, DefIdSet};
7use crate::hir::{self, BindingMode, ByRef, HirId, PatKind};
8
9pub struct EnumerateAndAdjust<I> {
10    enumerate: Enumerate<I>,
11    gap_pos: usize,
12    gap_len: usize,
13}
14
15impl<I> Iterator for EnumerateAndAdjust<I>
16where
17    I: Iterator,
18{
19    type Item = (usize, <I as Iterator>::Item);
20
21    fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
22        self.enumerate
23            .next()
24            .map(|(i, elem)| (if i < self.gap_pos { i } else { i + self.gap_len }, elem))
25    }
26
27    fn size_hint(&self) -> (usize, Option<usize>) {
28        self.enumerate.size_hint()
29    }
30}
31
32pub trait EnumerateAndAdjustIterator {
33    fn enumerate_and_adjust(
34        self,
35        expected_len: usize,
36        gap_pos: hir::DotDotPos,
37    ) -> EnumerateAndAdjust<Self>
38    where
39        Self: Sized;
40}
41
42impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
43    fn enumerate_and_adjust(
44        self,
45        expected_len: usize,
46        gap_pos: hir::DotDotPos,
47    ) -> EnumerateAndAdjust<Self>
48    where
49        Self: Sized,
50    {
51        let actual_len = self.len();
52        EnumerateAndAdjust {
53            enumerate: self.enumerate(),
54            gap_pos: gap_pos.as_opt_usize().unwrap_or(expected_len),
55            gap_len: expected_len - actual_len,
56        }
57    }
58}
59
60impl hir::Pat<'_> {
61    /// Call `f` on every "binding" in a pattern, e.g., on `a` in
62    /// `match foo() { Some(a) => (), None => () }`
63    pub fn each_binding(&self, mut f: impl FnMut(hir::BindingMode, HirId, Span, Ident)) {
64        self.walk_always(|p| {
65            if let PatKind::Binding(binding_mode, _, ident, _) = p.kind {
66                f(binding_mode, p.hir_id, p.span, ident);
67            }
68        });
69    }
70
71    /// Call `f` on every "binding" in a pattern, e.g., on `a` in
72    /// `match foo() { Some(a) => (), None => () }`.
73    ///
74    /// When encountering an or-pattern `p_0 | ... | p_n` only the first non-never pattern will be
75    /// visited. If they're all never patterns we visit nothing, which is ok since a never pattern
76    /// cannot have bindings.
77    pub fn each_binding_or_first(&self, f: &mut impl FnMut(hir::BindingMode, HirId, Span, Ident)) {
78        self.walk(|p| match &p.kind {
79            PatKind::Or(ps) => {
80                for p in *ps {
81                    if !p.is_never_pattern() {
82                        p.each_binding_or_first(f);
83                        break;
84                    }
85                }
86                false
87            }
88            PatKind::Binding(bm, _, ident, _) => {
89                f(*bm, p.hir_id, p.span, *ident);
90                true
91            }
92            _ => true,
93        })
94    }
95
96    pub fn simple_ident(&self) -> Option<Ident> {
97        match self.kind {
98            PatKind::Binding(BindingMode(ByRef::No, _), _, ident, None) => Some(ident),
99            _ => None,
100        }
101    }
102
103    /// Returns variants that are necessary to exist for the pattern to match.
104    pub fn necessary_variants(&self) -> Vec<DefId> {
105        let mut variants = vec![];
106        self.walk(|p| match &p.kind {
107            PatKind::Or(_) => false,
108            PatKind::Expr(hir::PatExpr {
109                kind: hir::PatExprKind::Path(hir::QPath::Resolved(_, path)),
110                ..
111            })
112            | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..)
113            | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => {
114                if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) =
115                    path.res
116                {
117                    variants.push(id);
118                }
119                true
120            }
121            _ => true,
122        });
123        // We remove duplicates by inserting into a hash set to avoid re-ordering
124        // the bounds
125        let mut duplicates = DefIdSet::default();
126        variants.retain(|def_id| duplicates.insert(*def_id));
127        variants
128    }
129
130    /// Checks if the pattern contains any `ref` or `ref mut` bindings, and if
131    /// yes whether it contains mutable or just immutables ones.
132    //
133    // FIXME(tschottdorf): this is problematic as the HIR is being scraped, but
134    // ref bindings are be implicit after #42640 (default match binding modes). See issue #44848.
135    pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
136        let mut result = None;
137        self.each_binding(|annotation, _, _, _| match annotation {
138            hir::BindingMode::REF if result.is_none() => result = Some(hir::Mutability::Not),
139            hir::BindingMode::REF_MUT => result = Some(hir::Mutability::Mut),
140            _ => {}
141        });
142        result
143    }
144}