]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ast_util.rs
Auto merge of #30457 - Manishearth:rollup, r=Manishearth
[rust.git] / src / libsyntax / ast_util.rs
1 // Copyright 2012-2014 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 ast::*;
12 use ast;
13 use codemap;
14 use codemap::Span;
15 use parse::token;
16 use print::pprust;
17 use ptr::P;
18 use visit::{FnKind, Visitor};
19 use visit;
20
21 use std::cmp;
22 use std::u32;
23
24 pub fn path_name_i(idents: &[Ident]) -> String {
25     // FIXME: Bad copies (#2543 -- same for everything else that says "bad")
26     idents.iter().map(|i| i.to_string()).collect::<Vec<String>>().join("::")
27 }
28
29 pub fn is_path(e: P<Expr>) -> bool {
30     match e.node { ExprPath(..) => true, _ => false }
31 }
32
33
34 // convert a span and an identifier to the corresponding
35 // 1-segment path
36 pub fn ident_to_path(s: Span, identifier: Ident) -> Path {
37     ast::Path {
38         span: s,
39         global: false,
40         segments: vec!(
41             ast::PathSegment {
42                 identifier: identifier,
43                 parameters: ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
44                     lifetimes: Vec::new(),
45                     types: P::empty(),
46                     bindings: P::empty(),
47                 })
48             }
49         ),
50     }
51 }
52
53 // If path is a single segment ident path, return that ident. Otherwise, return
54 // None.
55 pub fn path_to_ident(path: &Path) -> Option<Ident> {
56     if path.segments.len() != 1 {
57         return None;
58     }
59
60     let segment = &path.segments[0];
61     if !segment.parameters.is_empty() {
62         return None;
63     }
64
65     Some(segment.identifier)
66 }
67
68 pub fn ident_to_pat(id: NodeId, s: Span, i: Ident) -> P<Pat> {
69     P(Pat {
70         id: id,
71         node: PatIdent(BindByValue(MutImmutable), codemap::Spanned{span:s, node:i}, None),
72         span: s
73     })
74 }
75
76 /// Generate a "pretty" name for an `impl` from its type and trait.
77 /// This is designed so that symbols of `impl`'d methods give some
78 /// hint of where they came from, (previously they would all just be
79 /// listed as `__extensions__::method_name::hash`, with no indication
80 /// of the type).
81 pub fn impl_pretty_name(trait_ref: &Option<TraitRef>, ty: Option<&Ty>) -> Ident {
82     let mut pretty = match ty {
83         Some(t) => pprust::ty_to_string(t),
84         None => String::from("..")
85     };
86
87     match *trait_ref {
88         Some(ref trait_ref) => {
89             pretty.push('.');
90             pretty.push_str(&pprust::path_to_string(&trait_ref.path));
91         }
92         None => {}
93     }
94     token::gensym_ident(&pretty[..])
95 }
96
97 pub fn struct_field_visibility(field: ast::StructField) -> Visibility {
98     match field.node.kind {
99         ast::NamedField(_, v) | ast::UnnamedField(v) => v
100     }
101 }
102
103 // ______________________________________________________________________
104 // Enumerating the IDs which appear in an AST
105
106 #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug)]
107 pub struct IdRange {
108     pub min: NodeId,
109     pub max: NodeId,
110 }
111
112 impl IdRange {
113     pub fn max() -> IdRange {
114         IdRange {
115             min: u32::MAX,
116             max: u32::MIN,
117         }
118     }
119
120     pub fn empty(&self) -> bool {
121         self.min >= self.max
122     }
123
124     pub fn add(&mut self, id: NodeId) {
125         self.min = cmp::min(self.min, id);
126         self.max = cmp::max(self.max, id + 1);
127     }
128 }
129
130 pub trait IdVisitingOperation {
131     fn visit_id(&mut self, node_id: NodeId);
132 }
133
134 /// A visitor that applies its operation to all of the node IDs
135 /// in a visitable thing.
136
137 pub struct IdVisitor<'a, O:'a> {
138     pub operation: &'a mut O,
139     pub visited_outermost: bool,
140 }
141
142 impl<'a, O: IdVisitingOperation> IdVisitor<'a, O> {
143     fn visit_generics_helper(&mut self, generics: &Generics) {
144         for type_parameter in generics.ty_params.iter() {
145             self.operation.visit_id(type_parameter.id)
146         }
147         for lifetime in &generics.lifetimes {
148             self.operation.visit_id(lifetime.lifetime.id)
149         }
150     }
151 }
152
153 impl<'a, 'v, O: IdVisitingOperation> Visitor<'v> for IdVisitor<'a, O> {
154     fn visit_mod(&mut self,
155                  module: &Mod,
156                  _: Span,
157                  node_id: NodeId) {
158         self.operation.visit_id(node_id);
159         visit::walk_mod(self, module)
160     }
161
162     fn visit_foreign_item(&mut self, foreign_item: &ForeignItem) {
163         self.operation.visit_id(foreign_item.id);
164         visit::walk_foreign_item(self, foreign_item)
165     }
166
167     fn visit_item(&mut self, item: &Item) {
168         if self.visited_outermost {
169             return
170         } else {
171             self.visited_outermost = true
172         }
173
174         self.operation.visit_id(item.id);
175         match item.node {
176             ItemUse(ref view_path) => {
177                 match view_path.node {
178                     ViewPathSimple(_, _) |
179                     ViewPathGlob(_) => {}
180                     ViewPathList(_, ref paths) => {
181                         for path in paths {
182                             self.operation.visit_id(path.node.id())
183                         }
184                     }
185                 }
186             }
187             _ => {}
188         }
189
190         visit::walk_item(self, item);
191
192         self.visited_outermost = false
193     }
194
195     fn visit_local(&mut self, local: &Local) {
196         self.operation.visit_id(local.id);
197         visit::walk_local(self, local)
198     }
199
200     fn visit_block(&mut self, block: &Block) {
201         self.operation.visit_id(block.id);
202         visit::walk_block(self, block)
203     }
204
205     fn visit_stmt(&mut self, statement: &Stmt) {
206         self.operation
207             .visit_id(statement.node.id().expect("attempted to visit unexpanded stmt"));
208         visit::walk_stmt(self, statement)
209     }
210
211     fn visit_pat(&mut self, pattern: &Pat) {
212         self.operation.visit_id(pattern.id);
213         visit::walk_pat(self, pattern)
214     }
215
216     fn visit_expr(&mut self, expression: &Expr) {
217         self.operation.visit_id(expression.id);
218         visit::walk_expr(self, expression)
219     }
220
221     fn visit_ty(&mut self, typ: &Ty) {
222         self.operation.visit_id(typ.id);
223         visit::walk_ty(self, typ)
224     }
225
226     fn visit_generics(&mut self, generics: &Generics) {
227         self.visit_generics_helper(generics);
228         visit::walk_generics(self, generics)
229     }
230
231     fn visit_fn(&mut self,
232                 function_kind: visit::FnKind<'v>,
233                 function_declaration: &'v FnDecl,
234                 block: &'v Block,
235                 span: Span,
236                 node_id: NodeId) {
237         match function_kind {
238             FnKind::Method(..) if self.visited_outermost => return,
239             FnKind::Method(..) => self.visited_outermost = true,
240             _ => {}
241         }
242
243         self.operation.visit_id(node_id);
244
245         match function_kind {
246             FnKind::ItemFn(_, generics, _, _, _, _) => {
247                 self.visit_generics_helper(generics)
248             }
249             FnKind::Method(_, sig, _) => {
250                 self.visit_generics_helper(&sig.generics)
251             }
252             FnKind::Closure => {}
253         }
254
255         for argument in &function_declaration.inputs {
256             self.operation.visit_id(argument.id)
257         }
258
259         visit::walk_fn(self,
260                        function_kind,
261                        function_declaration,
262                        block,
263                        span);
264
265         if let FnKind::Method(..) = function_kind {
266             self.visited_outermost = false;
267         }
268     }
269
270     fn visit_struct_field(&mut self, struct_field: &StructField) {
271         self.operation.visit_id(struct_field.node.id);
272         visit::walk_struct_field(self, struct_field)
273     }
274
275     fn visit_variant_data(&mut self,
276                         struct_def: &VariantData,
277                         _: ast::Ident,
278                         _: &ast::Generics,
279                         _: NodeId,
280                         _: Span) {
281         self.operation.visit_id(struct_def.id());
282         visit::walk_struct_def(self, struct_def);
283     }
284
285     fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
286         self.operation.visit_id(ti.id);
287         visit::walk_trait_item(self, ti);
288     }
289
290     fn visit_impl_item(&mut self, ii: &ast::ImplItem) {
291         self.operation.visit_id(ii.id);
292         visit::walk_impl_item(self, ii);
293     }
294
295     fn visit_lifetime(&mut self, lifetime: &Lifetime) {
296         self.operation.visit_id(lifetime.id);
297     }
298
299     fn visit_lifetime_def(&mut self, def: &LifetimeDef) {
300         self.visit_lifetime(&def.lifetime);
301     }
302
303     fn visit_trait_ref(&mut self, trait_ref: &TraitRef) {
304         self.operation.visit_id(trait_ref.ref_id);
305         visit::walk_trait_ref(self, trait_ref);
306     }
307 }
308
309 pub struct IdRangeComputingVisitor {
310     pub result: IdRange,
311 }
312
313 impl IdRangeComputingVisitor {
314     pub fn new() -> IdRangeComputingVisitor {
315         IdRangeComputingVisitor { result: IdRange::max() }
316     }
317
318     pub fn result(&self) -> IdRange {
319         self.result
320     }
321 }
322
323 impl IdVisitingOperation for IdRangeComputingVisitor {
324     fn visit_id(&mut self, id: NodeId) {
325         self.result.add(id);
326     }
327 }
328
329 /// Computes the id range for a single fn body, ignoring nested items.
330 pub fn compute_id_range_for_fn_body(fk: FnKind,
331                                     decl: &FnDecl,
332                                     body: &Block,
333                                     sp: Span,
334                                     id: NodeId)
335                                     -> IdRange
336 {
337     let mut visitor = IdRangeComputingVisitor::new();
338     let mut id_visitor = IdVisitor {
339         operation: &mut visitor,
340         visited_outermost: false,
341     };
342     id_visitor.visit_fn(fk, decl, body, sp, id);
343     id_visitor.operation.result
344 }
345
346 /// Returns true if the given pattern consists solely of an identifier
347 /// and false otherwise.
348 pub fn pat_is_ident(pat: P<ast::Pat>) -> bool {
349     match pat.node {
350         ast::PatIdent(..) => true,
351         _ => false,
352     }
353 }
354
355 // are two paths equal when compared unhygienically?
356 // since I'm using this to replace ==, it seems appropriate
357 // to compare the span, global, etc. fields as well.
358 pub fn path_name_eq(a : &ast::Path, b : &ast::Path) -> bool {
359     (a.span == b.span)
360     && (a.global == b.global)
361     && (segments_name_eq(&a.segments[..], &b.segments[..]))
362 }
363
364 // are two arrays of segments equal when compared unhygienically?
365 pub fn segments_name_eq(a : &[ast::PathSegment], b : &[ast::PathSegment]) -> bool {
366     a.len() == b.len() &&
367     a.iter().zip(b).all(|(s, t)| {
368         s.identifier.name == t.identifier.name &&
369         // FIXME #7743: ident -> name problems in lifetime comparison?
370         // can types contain idents?
371         s.parameters == t.parameters
372     })
373 }
374
375 #[cfg(test)]
376 mod tests {
377     use ast::*;
378     use super::*;
379
380     fn ident_to_segment(id: Ident) -> PathSegment {
381         PathSegment {identifier: id,
382                      parameters: PathParameters::none()}
383     }
384
385     #[test] fn idents_name_eq_test() {
386         assert!(segments_name_eq(
387             &[Ident::new(Name(3),SyntaxContext(4)), Ident::new(Name(78),SyntaxContext(82))]
388                 .iter().cloned().map(ident_to_segment).collect::<Vec<PathSegment>>(),
389             &[Ident::new(Name(3),SyntaxContext(104)), Ident::new(Name(78),SyntaxContext(182))]
390                 .iter().cloned().map(ident_to_segment).collect::<Vec<PathSegment>>()));
391         assert!(!segments_name_eq(
392             &[Ident::new(Name(3),SyntaxContext(4)), Ident::new(Name(78),SyntaxContext(82))]
393                 .iter().cloned().map(ident_to_segment).collect::<Vec<PathSegment>>(),
394             &[Ident::new(Name(3),SyntaxContext(104)), Ident::new(Name(77),SyntaxContext(182))]
395                 .iter().cloned().map(ident_to_segment).collect::<Vec<PathSegment>>()));
396     }
397 }