]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/pat_util.rs
Auto merge of #44060 - taleks:issue-43205, r=arielb1
[rust.git] / src / librustc / hir / pat_util.rs
1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use hir::def::Def;
12 use hir::def_id::DefId;
13 use hir::{self, PatKind};
14 use syntax::ast;
15 use syntax::codemap::Spanned;
16 use syntax_pos::Span;
17
18 use std::iter::{Enumerate, ExactSizeIterator};
19
20 pub struct EnumerateAndAdjust<I> {
21     enumerate: Enumerate<I>,
22     gap_pos: usize,
23     gap_len: usize,
24 }
25
26 impl<I> Iterator for EnumerateAndAdjust<I> where I: Iterator {
27     type Item = (usize, <I as Iterator>::Item);
28
29     fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
30         self.enumerate.next().map(|(i, elem)| {
31             (if i < self.gap_pos { i } else { i + self.gap_len }, elem)
32         })
33     }
34 }
35
36 pub trait EnumerateAndAdjustIterator {
37     fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option<usize>)
38         -> EnumerateAndAdjust<Self> where Self: Sized;
39 }
40
41 impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
42     fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option<usize>)
43             -> EnumerateAndAdjust<Self> where Self: Sized {
44         let actual_len = self.len();
45         EnumerateAndAdjust {
46             enumerate: self.enumerate(),
47             gap_pos: if let Some(gap_pos) = gap_pos { gap_pos } else { expected_len },
48             gap_len: expected_len - actual_len,
49         }
50     }
51 }
52
53 impl hir::Pat {
54     pub fn is_refutable(&self) -> bool {
55         match self.node {
56             PatKind::Lit(_) |
57             PatKind::Range(..) |
58             PatKind::Path(hir::QPath::Resolved(Some(..), _)) |
59             PatKind::Path(hir::QPath::TypeRelative(..)) => true,
60
61             PatKind::Path(hir::QPath::Resolved(_, ref path)) |
62             PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..) |
63             PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => {
64                 match path.def {
65                     Def::Variant(..) | Def::VariantCtor(..) => true,
66                     _ => false
67                 }
68             }
69             PatKind::Slice(..) => true,
70             _ => false
71         }
72     }
73
74     pub fn is_const(&self) -> bool {
75         match self.node {
76             PatKind::Path(hir::QPath::TypeRelative(..)) => true,
77             PatKind::Path(hir::QPath::Resolved(_, ref path)) => {
78                 match path.def {
79                     Def::Const(..) | Def::AssociatedConst(..) => true,
80                     _ => false
81                 }
82             }
83             _ => false
84         }
85     }
86
87     /// Call `f` on every "binding" in a pattern, e.g., on `a` in
88     /// `match foo() { Some(a) => (), None => () }`
89     pub fn each_binding<F>(&self, mut f: F)
90         where F: FnMut(hir::BindingAnnotation, ast::NodeId, Span, &Spanned<ast::Name>),
91     {
92         self.walk(|p| {
93             if let PatKind::Binding(binding_mode, _, ref pth, _) = p.node {
94                 f(binding_mode, p.id, p.span, pth);
95             }
96             true
97         });
98     }
99
100     /// Checks if the pattern contains any patterns that bind something to
101     /// an ident, e.g. `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
102     pub fn contains_bindings(&self) -> bool {
103         let mut contains_bindings = false;
104         self.walk(|p| {
105             if let PatKind::Binding(..) = p.node {
106                 contains_bindings = true;
107                 false // there's at least one binding, can short circuit now.
108             } else {
109                 true
110             }
111         });
112         contains_bindings
113     }
114
115     /// Checks if the pattern contains any patterns that bind something to
116     /// an ident or wildcard, e.g. `foo`, or `Foo(_)`, `foo @ Bar(..)`,
117     pub fn contains_bindings_or_wild(&self) -> bool {
118         let mut contains_bindings = false;
119         self.walk(|p| {
120             match p.node {
121                 PatKind::Binding(..) | PatKind::Wild => {
122                     contains_bindings = true;
123                     false // there's at least one binding/wildcard, can short circuit now.
124                 }
125                 _ => true
126             }
127         });
128         contains_bindings
129     }
130
131     pub fn simple_name(&self) -> Option<ast::Name> {
132         match self.node {
133             PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ref path1, None) |
134             PatKind::Binding(hir::BindingAnnotation::Mutable, _, ref path1, None) =>
135                 Some(path1.node),
136             _ => None,
137         }
138     }
139
140     /// Return variants that are necessary to exist for the pattern to match.
141     pub fn necessary_variants(&self) -> Vec<DefId> {
142         let mut variants = vec![];
143         self.walk(|p| {
144             match p.node {
145                 PatKind::Path(hir::QPath::Resolved(_, ref path)) |
146                 PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..) |
147                 PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => {
148                     match path.def {
149                         Def::Variant(id) |
150                         Def::VariantCtor(id, ..) => variants.push(id),
151                         _ => ()
152                     }
153                 }
154                 _ => ()
155             }
156             true
157         });
158         variants.sort();
159         variants.dedup();
160         variants
161     }
162
163     /// Checks if the pattern contains any `ref` or `ref mut` bindings,
164     /// and if yes whether it contains mutable or just immutables ones.
165     ///
166     /// FIXME(tschottdorf): this is problematic as the HIR is being scraped,
167     /// but ref bindings may be implicit after #42640.
168     pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
169         let mut result = None;
170         self.each_binding(|annotation, _, _, _| {
171             match annotation {
172                 hir::BindingAnnotation::Ref => {
173                     match result {
174                         None | Some(hir::MutImmutable) => result = Some(hir::MutImmutable),
175                         _ => (),
176                     }
177                 }
178                 hir::BindingAnnotation::RefMut => result = Some(hir::MutMutable),
179                 _ => (),
180             }
181         });
182         result
183     }
184 }
185
186 impl hir::Arm {
187     /// Checks if the patterns for this arm contain any `ref` or `ref mut`
188     /// bindings, and if yes whether its containing mutable ones or just immutables ones.
189     pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
190         // FIXME(tschottdorf): contains_explicit_ref_binding() must be removed
191         // for #42640.
192         self.pats.iter()
193                  .filter_map(|pat| pat.contains_explicit_ref_binding())
194                  .max_by_key(|m| match *m {
195                     hir::MutMutable => 1,
196                     hir::MutImmutable => 0,
197                  })
198     }
199 }