]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/pat_util.rs
Rollup merge of #26593 - GuillaumeGomez:patch-2, r=Manishearth
[rust.git] / src / librustc / middle / 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 middle::def::*;
12 use middle::ty;
13 use util::nodemap::FnvHashMap;
14
15 use syntax::ast;
16 use syntax::ast_util::walk_pat;
17 use syntax::codemap::{Span, DUMMY_SP};
18
19 pub type PatIdMap = FnvHashMap<ast::Ident, ast::NodeId>;
20
21 // This is used because same-named variables in alternative patterns need to
22 // use the NodeId of their namesake in the first pattern.
23 pub fn pat_id_map(dm: &DefMap, pat: &ast::Pat) -> PatIdMap {
24     let mut map = FnvHashMap();
25     pat_bindings(dm, pat, |_bm, p_id, _s, path1| {
26         map.insert(path1.node, p_id);
27     });
28     map
29 }
30
31 pub fn pat_is_refutable(dm: &DefMap, pat: &ast::Pat) -> bool {
32     match pat.node {
33         ast::PatLit(_) | ast::PatRange(_, _) | ast::PatQPath(..) => true,
34         ast::PatEnum(_, _) |
35         ast::PatIdent(_, _, None) |
36         ast::PatStruct(..) => {
37             match dm.borrow().get(&pat.id).map(|d| d.full_def()) {
38                 Some(DefVariant(..)) => true,
39                 _ => false
40             }
41         }
42         ast::PatVec(_, _, _) => true,
43         _ => false
44     }
45 }
46
47 pub fn pat_is_variant_or_struct(dm: &DefMap, pat: &ast::Pat) -> bool {
48     match pat.node {
49         ast::PatEnum(_, _) |
50         ast::PatIdent(_, _, None) |
51         ast::PatStruct(..) => {
52             match dm.borrow().get(&pat.id).map(|d| d.full_def()) {
53                 Some(DefVariant(..)) | Some(DefStruct(..)) => true,
54                 _ => false
55             }
56         }
57         _ => false
58     }
59 }
60
61 pub fn pat_is_const(dm: &DefMap, pat: &ast::Pat) -> bool {
62     match pat.node {
63         ast::PatIdent(_, _, None) | ast::PatEnum(..) | ast::PatQPath(..) => {
64             match dm.borrow().get(&pat.id).map(|d| d.full_def()) {
65                 Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true,
66                 _ => false
67             }
68         }
69         _ => false
70     }
71 }
72
73 // Same as above, except that partially-resolved defs cause `false` to be
74 // returned instead of a panic.
75 pub fn pat_is_resolved_const(dm: &DefMap, pat: &ast::Pat) -> bool {
76     match pat.node {
77         ast::PatIdent(_, _, None) | ast::PatEnum(..) | ast::PatQPath(..) => {
78             match dm.borrow().get(&pat.id)
79                     .and_then(|d| if d.depth == 0 { Some(d.base_def) }
80                                   else { None } ) {
81                 Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true,
82                 _ => false
83             }
84         }
85         _ => false
86     }
87 }
88
89 pub fn pat_is_binding(dm: &DefMap, pat: &ast::Pat) -> bool {
90     match pat.node {
91         ast::PatIdent(..) => {
92             !pat_is_variant_or_struct(dm, pat) &&
93             !pat_is_const(dm, pat)
94         }
95         _ => false
96     }
97 }
98
99 pub fn pat_is_binding_or_wild(dm: &DefMap, pat: &ast::Pat) -> bool {
100     match pat.node {
101         ast::PatIdent(..) => pat_is_binding(dm, pat),
102         ast::PatWild(_) => true,
103         _ => false
104     }
105 }
106
107 /// Call `it` on every "binding" in a pattern, e.g., on `a` in
108 /// `match foo() { Some(a) => (), None => () }`
109 pub fn pat_bindings<I>(dm: &DefMap, pat: &ast::Pat, mut it: I) where
110     I: FnMut(ast::BindingMode, ast::NodeId, Span, &ast::SpannedIdent),
111 {
112     walk_pat(pat, |p| {
113         match p.node {
114           ast::PatIdent(binding_mode, ref pth, _) if pat_is_binding(dm, p) => {
115             it(binding_mode, p.id, p.span, pth);
116           }
117           _ => {}
118         }
119         true
120     });
121 }
122
123 /// Checks if the pattern contains any patterns that bind something to
124 /// an ident, e.g. `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
125 pub fn pat_contains_bindings(dm: &DefMap, pat: &ast::Pat) -> bool {
126     let mut contains_bindings = false;
127     walk_pat(pat, |p| {
128         if pat_is_binding(dm, p) {
129             contains_bindings = true;
130             false // there's at least one binding, can short circuit now.
131         } else {
132             true
133         }
134     });
135     contains_bindings
136 }
137
138 /// Checks if the pattern contains any `ref` or `ref mut` bindings,
139 /// and if yes wether its containing mutable ones or just immutables ones.
140 pub fn pat_contains_ref_binding(dm: &DefMap, pat: &ast::Pat) -> Option<ast::Mutability> {
141     let mut result = None;
142     pat_bindings(dm, pat, |mode, _, _, _| {
143         match mode {
144             ast::BindingMode::BindByRef(m) => {
145                 // Pick Mutable as maximum
146                 match result {
147                     None | Some(ast::MutImmutable) => result = Some(m),
148                     _ => (),
149                 }
150             }
151             ast::BindingMode::BindByValue(_) => { }
152         }
153     });
154     result
155 }
156
157 /// Checks if the patterns for this arm contain any `ref` or `ref mut`
158 /// bindings, and if yes wether its containing mutable ones or just immutables ones.
159 pub fn arm_contains_ref_binding(dm: &DefMap, arm: &ast::Arm) -> Option<ast::Mutability> {
160     arm.pats.iter()
161             .filter_map(|pat| pat_contains_ref_binding(dm, pat))
162             .max_by(|m| match *m {
163                 ast::MutMutable => 1,
164                 ast::MutImmutable => 0,
165             })
166 }
167
168 /// Checks if the pattern contains any patterns that bind something to
169 /// an ident or wildcard, e.g. `foo`, or `Foo(_)`, `foo @ Bar(..)`,
170 pub fn pat_contains_bindings_or_wild(dm: &DefMap, pat: &ast::Pat) -> bool {
171     let mut contains_bindings = false;
172     walk_pat(pat, |p| {
173         if pat_is_binding_or_wild(dm, p) {
174             contains_bindings = true;
175             false // there's at least one binding/wildcard, can short circuit now.
176         } else {
177             true
178         }
179     });
180     contains_bindings
181 }
182
183 pub fn simple_identifier<'a>(pat: &'a ast::Pat) -> Option<&'a ast::Ident> {
184     match pat.node {
185         ast::PatIdent(ast::BindByValue(_), ref path1, None) => {
186             Some(&path1.node)
187         }
188         _ => {
189             None
190         }
191     }
192 }
193
194 pub fn def_to_path(tcx: &ty::ctxt, id: ast::DefId) -> ast::Path {
195     tcx.with_path(id, |path| ast::Path {
196         global: false,
197         segments: path.last().map(|elem| ast::PathSegment {
198             identifier: ast::Ident::new(elem.name()),
199             parameters: ast::PathParameters::none(),
200         }).into_iter().collect(),
201         span: DUMMY_SP,
202     })
203 }
204
205 /// Return variants that are necessary to exist for the pattern to match.
206 pub fn necessary_variants(dm: &DefMap, pat: &ast::Pat) -> Vec<ast::NodeId> {
207     let mut variants = vec![];
208     walk_pat(pat, |p| {
209         match p.node {
210             ast::PatEnum(_, _) |
211             ast::PatIdent(_, _, None) |
212             ast::PatStruct(..) => {
213                 match dm.borrow().get(&p.id) {
214                     Some(&PathResolution { base_def: DefVariant(_, id, _), .. }) => {
215                         variants.push(id.node);
216                     }
217                     _ => ()
218                 }
219             }
220             _ => ()
221         }
222         true
223     });
224     variants.sort();
225     variants.dedup();
226     variants
227 }