]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/pat_util.rs
dec41fdfc3b50c35a4693339c61948d6488a3407
[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::*;
12 use hir::def_id::DefId;
13 use hir::{self, PatKind};
14 use ty::TyCtxt;
15 use syntax::ast;
16 use syntax::codemap::Spanned;
17 use syntax_pos::{Span, DUMMY_SP};
18
19 use std::iter::{Enumerate, ExactSizeIterator};
20
21 pub struct EnumerateAndAdjust<I> {
22     enumerate: Enumerate<I>,
23     gap_pos: usize,
24     gap_len: usize,
25 }
26
27 impl<I> Iterator for EnumerateAndAdjust<I> where I: Iterator {
28     type Item = (usize, <I as Iterator>::Item);
29
30     fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
31         self.enumerate.next().map(|(i, elem)| {
32             (if i < self.gap_pos { i } else { i + self.gap_len }, elem)
33         })
34     }
35 }
36
37 pub trait EnumerateAndAdjustIterator {
38     fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option<usize>)
39         -> EnumerateAndAdjust<Self> where Self: Sized;
40 }
41
42 impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
43     fn enumerate_and_adjust(self, expected_len: usize, gap_pos: Option<usize>)
44             -> EnumerateAndAdjust<Self> where Self: Sized {
45         let actual_len = self.len();
46         EnumerateAndAdjust {
47             enumerate: self.enumerate(),
48             gap_pos: if let Some(gap_pos) = gap_pos { gap_pos } else { expected_len },
49             gap_len: expected_len - actual_len,
50         }
51     }
52 }
53
54 pub fn pat_is_refutable(dm: &DefMap, pat: &hir::Pat) -> bool {
55     match pat.node {
56         PatKind::Lit(_) | PatKind::Range(..) | PatKind::Path(Some(..), _) => true,
57         PatKind::TupleStruct(..) |
58         PatKind::Path(..) |
59         PatKind::Struct(..) => {
60             match dm.get(&pat.id).map(|d| d.full_def()) {
61                 Some(Def::Variant(..)) => true,
62                 _ => false
63             }
64         }
65         PatKind::Vec(..) => true,
66         _ => false
67     }
68 }
69
70 pub fn pat_is_const(dm: &DefMap, pat: &hir::Pat) -> bool {
71     match pat.node {
72         PatKind::Path(..) => {
73             match dm.get(&pat.id).map(|d| d.full_def()) {
74                 Some(Def::Const(..)) | Some(Def::AssociatedConst(..)) => true,
75                 _ => false
76             }
77         }
78         _ => false
79     }
80 }
81
82 /// Call `f` on every "binding" in a pattern, e.g., on `a` in
83 /// `match foo() { Some(a) => (), None => () }`
84 pub fn pat_bindings<F>(pat: &hir::Pat, mut f: F)
85     where F: FnMut(hir::BindingMode, ast::NodeId, Span, &Spanned<ast::Name>),
86 {
87     pat.walk(|p| {
88         if let PatKind::Binding(binding_mode, ref pth, _) = p.node {
89             f(binding_mode, p.id, p.span, pth);
90         }
91         true
92     });
93 }
94
95 /// Checks if the pattern contains any patterns that bind something to
96 /// an ident, e.g. `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
97 pub fn pat_contains_bindings(pat: &hir::Pat) -> bool {
98     let mut contains_bindings = false;
99     pat.walk(|p| {
100         if let PatKind::Binding(..) = p.node {
101             contains_bindings = true;
102             false // there's at least one binding, can short circuit now.
103         } else {
104             true
105         }
106     });
107     contains_bindings
108 }
109
110 /// Checks if the pattern contains any `ref` or `ref mut` bindings,
111 /// and if yes whether its containing mutable ones or just immutables ones.
112 pub fn pat_contains_ref_binding(pat: &hir::Pat) -> Option<hir::Mutability> {
113     let mut result = None;
114     pat_bindings(pat, |mode, _, _, _| {
115         if let hir::BindingMode::BindByRef(m) = mode {
116             // Pick Mutable as maximum
117             match result {
118                 None | Some(hir::MutImmutable) => result = Some(m),
119                 _ => (),
120             }
121         }
122     });
123     result
124 }
125
126 /// Checks if the patterns for this arm contain any `ref` or `ref mut`
127 /// bindings, and if yes whether its containing mutable ones or just immutables ones.
128 pub fn arm_contains_ref_binding(arm: &hir::Arm) -> Option<hir::Mutability> {
129     arm.pats.iter()
130             .filter_map(|pat| pat_contains_ref_binding(pat))
131             .max_by_key(|m| match *m {
132                 hir::MutMutable => 1,
133                 hir::MutImmutable => 0,
134             })
135 }
136
137 /// Checks if the pattern contains any patterns that bind something to
138 /// an ident or wildcard, e.g. `foo`, or `Foo(_)`, `foo @ Bar(..)`,
139 pub fn pat_contains_bindings_or_wild(pat: &hir::Pat) -> bool {
140     let mut contains_bindings = false;
141     pat.walk(|p| {
142         match p.node {
143             PatKind::Binding(..) | PatKind::Wild => {
144                 contains_bindings = true;
145                 false // there's at least one binding/wildcard, can short circuit now.
146             }
147             _ => true
148         }
149     });
150     contains_bindings
151 }
152
153 pub fn simple_name<'a>(pat: &'a hir::Pat) -> Option<ast::Name> {
154     match pat.node {
155         PatKind::Binding(hir::BindByValue(..), ref path1, None) => {
156             Some(path1.node)
157         }
158         _ => {
159             None
160         }
161     }
162 }
163
164 pub fn def_to_path<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> hir::Path {
165     hir::Path::from_name(DUMMY_SP, tcx.item_name(id))
166 }
167
168 /// Return variants that are necessary to exist for the pattern to match.
169 pub fn necessary_variants(dm: &DefMap, pat: &hir::Pat) -> Vec<DefId> {
170     let mut variants = vec![];
171     pat.walk(|p| {
172         match p.node {
173             PatKind::TupleStruct(..) |
174             PatKind::Path(..) |
175             PatKind::Struct(..) => {
176                 match dm.get(&p.id) {
177                     Some(&PathResolution { base_def: Def::Variant(id), .. }) => {
178                         variants.push(id);
179                     }
180                     _ => ()
181                 }
182             }
183             _ => ()
184         }
185         true
186     });
187     variants.sort();
188     variants.dedup();
189     variants
190 }