]> git.lizzy.rs Git - rust.git/blob - src/librustc_hir/pat_util.rs
Rollup merge of #74762 - ssomers:btree_no_root_in_remove_kv_tracking, r=Mark-Simulacrum
[rust.git] / src / librustc_hir / pat_util.rs
1 use crate::def::{CtorOf, DefKind, Res};
2 use crate::def_id::DefId;
3 use crate::hir::{self, HirId, PatKind};
4 use rustc_span::symbol::Ident;
5 use rustc_span::Span;
6
7 use std::iter::{Enumerate, ExactSizeIterator};
8
9 pub struct EnumerateAndAdjust<I> {
10     enumerate: Enumerate<I>,
11     gap_pos: usize,
12     gap_len: usize,
13 }
14
15 impl<I> Iterator for EnumerateAndAdjust<I>
16 where
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
32 pub trait EnumerateAndAdjustIterator {
33     fn enumerate_and_adjust(
34         self,
35         expected_len: usize,
36         gap_pos: Option<usize>,
37     ) -> EnumerateAndAdjust<Self>
38     where
39         Self: Sized;
40 }
41
42 impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
43     fn enumerate_and_adjust(
44         self,
45         expected_len: usize,
46         gap_pos: Option<usize>,
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.unwrap_or(expected_len),
55             gap_len: expected_len - actual_len,
56         }
57     }
58 }
59
60 impl hir::Pat<'_> {
61     pub fn is_refutable(&self) -> bool {
62         match self.kind {
63             PatKind::Lit(_)
64             | PatKind::Range(..)
65             | PatKind::Path(hir::QPath::Resolved(Some(..), _) | hir::QPath::TypeRelative(..)) => {
66                 true
67             }
68
69             PatKind::Path(hir::QPath::Resolved(_, ref path))
70             | PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..)
71             | PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => match path.res {
72                 Res::Def(DefKind::Variant, _) => true,
73                 _ => false,
74             },
75             PatKind::Slice(..) => true,
76             _ => false,
77         }
78     }
79
80     /// Call `f` on every "binding" in a pattern, e.g., on `a` in
81     /// `match foo() { Some(a) => (), None => () }`
82     pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, Ident)) {
83         self.walk_always(|p| {
84             if let PatKind::Binding(binding_mode, _, ident, _) = p.kind {
85                 f(binding_mode, p.hir_id, p.span, ident);
86             }
87         });
88     }
89
90     /// Call `f` on every "binding" in a pattern, e.g., on `a` in
91     /// `match foo() { Some(a) => (), None => () }`.
92     ///
93     /// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited.
94     pub fn each_binding_or_first(
95         &self,
96         f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident),
97     ) {
98         self.walk(|p| match &p.kind {
99             PatKind::Or(ps) => {
100                 ps[0].each_binding_or_first(f);
101                 false
102             }
103             PatKind::Binding(bm, _, ident, _) => {
104                 f(*bm, p.hir_id, p.span, *ident);
105                 true
106             }
107             _ => true,
108         })
109     }
110
111     /// Checks if the pattern contains any patterns that bind something to
112     /// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
113     pub fn contains_bindings(&self) -> bool {
114         self.satisfies(|p| match p.kind {
115             PatKind::Binding(..) => true,
116             _ => false,
117         })
118     }
119
120     /// Checks if the pattern contains any patterns that bind something to
121     /// an ident or wildcard, e.g., `foo`, or `Foo(_)`, `foo @ Bar(..)`,
122     pub fn contains_bindings_or_wild(&self) -> bool {
123         self.satisfies(|p| match p.kind {
124             PatKind::Binding(..) | PatKind::Wild => true,
125             _ => false,
126         })
127     }
128
129     /// Checks if the pattern satisfies the given predicate on some sub-pattern.
130     fn satisfies(&self, pred: impl Fn(&hir::Pat<'_>) -> bool) -> bool {
131         let mut satisfies = false;
132         self.walk_short(|p| {
133             if pred(p) {
134                 satisfies = true;
135                 false // Found one, can short circuit now.
136             } else {
137                 true
138             }
139         });
140         satisfies
141     }
142
143     pub fn simple_ident(&self) -> Option<Ident> {
144         match self.kind {
145             PatKind::Binding(
146                 hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable,
147                 _,
148                 ident,
149                 None,
150             ) => Some(ident),
151             _ => None,
152         }
153     }
154
155     /// Returns variants that are necessary to exist for the pattern to match.
156     pub fn necessary_variants(&self) -> Vec<DefId> {
157         let mut variants = vec![];
158         self.walk(|p| match &p.kind {
159             PatKind::Or(_) => false,
160             PatKind::Path(hir::QPath::Resolved(_, path))
161             | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..)
162             | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => {
163                 if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) =
164                     path.res
165                 {
166                     variants.push(id);
167                 }
168                 true
169             }
170             _ => true,
171         });
172         variants.sort();
173         variants.dedup();
174         variants
175     }
176
177     /// Checks if the pattern contains any `ref` or `ref mut` bindings, and if
178     /// yes whether it contains mutable or just immutables ones.
179     //
180     // FIXME(tschottdorf): this is problematic as the HIR is being scraped, but
181     // ref bindings are be implicit after #42640 (default match binding modes). See issue #44848.
182     pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
183         let mut result = None;
184         self.each_binding(|annotation, _, _, _| match annotation {
185             hir::BindingAnnotation::Ref => match result {
186                 None | Some(hir::Mutability::Not) => result = Some(hir::Mutability::Not),
187                 _ => {}
188             },
189             hir::BindingAnnotation::RefMut => result = Some(hir::Mutability::Mut),
190             _ => {}
191         });
192         result
193     }
194 }