]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/pat_util.rs
Rollup merge of #59432 - phansch:compiletest_docs, r=alexcrichton
[rust.git] / src / librustc / hir / pat_util.rs
1 use crate::hir::def::{CtorOf, Def};
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.node {
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.def {
58                     Def::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<F>(&self, mut f: F)
70         where F: FnMut(hir::BindingAnnotation, HirId, Span, ast::Ident),
71     {
72         self.walk(|p| {
73             if let PatKind::Binding(binding_mode, _, ident, _) = p.node {
74                 f(binding_mode, p.hir_id, p.span, ident);
75             }
76             true
77         });
78     }
79
80     /// Checks if the pattern contains any patterns that bind something to
81     /// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
82     pub fn contains_bindings(&self) -> bool {
83         let mut contains_bindings = false;
84         self.walk(|p| {
85             if let PatKind::Binding(..) = p.node {
86                 contains_bindings = true;
87                 false // there's at least one binding, can short circuit now.
88             } else {
89                 true
90             }
91         });
92         contains_bindings
93     }
94
95     /// Checks if the pattern contains any patterns that bind something to
96     /// an ident or wildcard, e.g., `foo`, or `Foo(_)`, `foo @ Bar(..)`,
97     pub fn contains_bindings_or_wild(&self) -> bool {
98         let mut contains_bindings = false;
99         self.walk(|p| {
100             match p.node {
101                 PatKind::Binding(..) | PatKind::Wild => {
102                     contains_bindings = true;
103                     false // there's at least one binding/wildcard, can short circuit now.
104                 }
105                 _ => true
106             }
107         });
108         contains_bindings
109     }
110
111     pub fn simple_ident(&self) -> Option<ast::Ident> {
112         match self.node {
113             PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) |
114             PatKind::Binding(hir::BindingAnnotation::Mutable, _, ident, None) => Some(ident),
115             _ => None,
116         }
117     }
118
119     /// Returns variants that are necessary to exist for the pattern to match.
120     pub fn necessary_variants(&self) -> Vec<DefId> {
121         let mut variants = vec![];
122         self.walk(|p| {
123             match p.node {
124                 PatKind::Path(hir::QPath::Resolved(_, ref path)) |
125                 PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..) |
126                 PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => {
127                     match path.def {
128                         Def::Variant(id) => variants.push(id),
129                         Def::Ctor(id, CtorOf::Variant, ..) => variants.push(id),
130                         _ => ()
131                     }
132                 }
133                 _ => ()
134             }
135             true
136         });
137         variants.sort();
138         variants.dedup();
139         variants
140     }
141
142     /// Checks if the pattern contains any `ref` or `ref mut` bindings, and if
143     /// yes whether it contains mutable or just immutables ones.
144     //
145     // FIXME(tschottdorf): this is problematic as the HIR is being scraped, but
146     // ref bindings are be implicit after #42640 (default match binding modes). See issue #44848.
147     pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
148         let mut result = None;
149         self.each_binding(|annotation, _, _, _| {
150             match annotation {
151                 hir::BindingAnnotation::Ref => {
152                     match result {
153                         None | Some(hir::MutImmutable) => result = Some(hir::MutImmutable),
154                         _ => (),
155                     }
156                 }
157                 hir::BindingAnnotation::RefMut => result = Some(hir::MutMutable),
158                 _ => (),
159             }
160         });
161         result
162     }
163 }
164
165 impl hir::Arm {
166     /// Checks if the patterns for this arm contain any `ref` or `ref mut`
167     /// bindings, and if yes whether its containing mutable ones or just immutables ones.
168     pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
169         // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed
170         // for #42640 (default match binding modes).
171         //
172         // See #44848.
173         self.pats.iter()
174                  .filter_map(|pat| pat.contains_explicit_ref_binding())
175                  .max_by_key(|m| match *m {
176                     hir::MutMutable => 1,
177                     hir::MutImmutable => 0,
178                  })
179     }
180 }