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