]> git.lizzy.rs Git - rust.git/blob - src/visitor.rs
Merge pull request #1609 from topecongiro/nested-tuple
[rust.git] / src / visitor.rs
1 // Copyright 2015 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 std::cmp;
12
13 use syntax::{ast, ptr, visit};
14 use syntax::codemap::{self, CodeMap, Span, BytePos};
15 use syntax::parse::ParseSess;
16
17 use strings::string_buffer::StringBuffer;
18
19 use {Indent, Shape};
20 use utils;
21 use codemap::{LineRangeUtils, SpanUtils};
22 use comment::FindUncommented;
23 use config::Config;
24 use rewrite::{Rewrite, RewriteContext};
25 use comment::rewrite_comment;
26 use macros::{rewrite_macro, MacroPosition};
27 use items::{rewrite_static, rewrite_associated_type, rewrite_associated_impl_type,
28             rewrite_type_alias, format_impl, format_trait};
29
30 fn is_use_item(item: &ast::Item) -> bool {
31     match item.node {
32         ast::ItemKind::Use(_) => true,
33         _ => false,
34     }
35 }
36
37 fn item_bound(item: &ast::Item) -> Span {
38     item.attrs
39         .iter()
40         .map(|attr| attr.span)
41         .fold(item.span, |bound, span| {
42             Span {
43                 lo: cmp::min(bound.lo, span.lo),
44                 hi: cmp::max(bound.hi, span.hi),
45                 expn_id: span.expn_id,
46             }
47         })
48 }
49
50 pub struct FmtVisitor<'a> {
51     pub parse_session: &'a ParseSess,
52     pub codemap: &'a CodeMap,
53     pub buffer: StringBuffer,
54     pub last_pos: BytePos,
55     // FIXME: use an RAII util or closure for indenting
56     pub block_indent: Indent,
57     pub config: &'a Config,
58     pub failed: bool,
59 }
60
61 impl<'a> FmtVisitor<'a> {
62     fn visit_stmt(&mut self, stmt: &ast::Stmt) {
63         debug!("visit_stmt: {:?} {:?}",
64                self.codemap.lookup_char_pos(stmt.span.lo),
65                self.codemap.lookup_char_pos(stmt.span.hi));
66
67         // FIXME(#434): Move this check to somewhere more central, eg Rewrite.
68         if !self.config
69                .file_lines()
70                .intersects(&self.codemap.lookup_line_range(stmt.span)) {
71             return;
72         }
73
74         match stmt.node {
75             ast::StmtKind::Item(ref item) => {
76                 self.visit_item(item);
77             }
78             ast::StmtKind::Local(..) |
79             ast::StmtKind::Expr(..) |
80             ast::StmtKind::Semi(..) => {
81                 let rewrite =
82                     stmt.rewrite(&self.get_context(),
83                                  Shape::indented(self.block_indent, self.config));
84                 self.push_rewrite(stmt.span, rewrite);
85             }
86             ast::StmtKind::Mac(ref mac) => {
87                 let (ref mac, _macro_style, _) = **mac;
88                 self.visit_mac(mac, None, MacroPosition::Statement);
89                 self.format_missing(stmt.span.hi);
90             }
91         }
92     }
93
94     pub fn visit_block(&mut self, b: &ast::Block) {
95         debug!("visit_block: {:?} {:?}",
96                self.codemap.lookup_char_pos(b.span.lo),
97                self.codemap.lookup_char_pos(b.span.hi));
98
99         // Check if this block has braces.
100         let snippet = self.snippet(b.span);
101         let has_braces = snippet.starts_with('{') || snippet.starts_with("unsafe");
102         let brace_compensation = if has_braces { BytePos(1) } else { BytePos(0) };
103
104         self.last_pos = self.last_pos + brace_compensation;
105         self.block_indent = self.block_indent.block_indent(self.config);
106         self.buffer.push_str("{");
107
108         for stmt in &b.stmts {
109             self.visit_stmt(stmt)
110         }
111
112         if !b.stmts.is_empty() {
113             if let Some(expr) = utils::stmt_expr(&b.stmts[b.stmts.len() - 1]) {
114                 if utils::semicolon_for_expr(expr) {
115                     self.buffer.push_str(";");
116                 }
117             }
118         }
119
120         // FIXME: we should compress any newlines here to just one
121         self.format_missing_with_indent(source!(self, b.span).hi - brace_compensation);
122         self.close_block();
123         self.last_pos = source!(self, b.span).hi;
124     }
125
126     // FIXME: this is a terrible hack to indent the comments between the last
127     // item in the block and the closing brace to the block's level.
128     // The closing brace itself, however, should be indented at a shallower
129     // level.
130     fn close_block(&mut self) {
131         let total_len = self.buffer.len;
132         let chars_too_many = if self.config.hard_tabs() {
133             1
134         } else {
135             self.config.tab_spaces()
136         };
137         self.buffer.truncate(total_len - chars_too_many);
138         self.buffer.push_str("}");
139         self.block_indent = self.block_indent.block_unindent(self.config);
140     }
141
142     // Note that this only gets called for function definitions. Required methods
143     // on traits do not get handled here.
144     fn visit_fn(&mut self,
145                 fk: visit::FnKind,
146                 fd: &ast::FnDecl,
147                 s: Span,
148                 _: ast::NodeId,
149                 defaultness: ast::Defaultness) {
150         let indent = self.block_indent;
151         let block;
152         let rewrite = match fk {
153             visit::FnKind::ItemFn(ident, generics, unsafety, constness, abi, vis, b) => {
154                 block = b;
155                 self.rewrite_fn(indent,
156                                 ident,
157                                 fd,
158                                 generics,
159                                 unsafety,
160                                 constness.node,
161                                 defaultness,
162                                 abi,
163                                 vis,
164                                 codemap::mk_sp(s.lo, b.span.lo),
165                                 &b)
166             }
167             visit::FnKind::Method(ident, sig, vis, b) => {
168                 block = b;
169                 self.rewrite_fn(indent,
170                                 ident,
171                                 fd,
172                                 &sig.generics,
173                                 sig.unsafety,
174                                 sig.constness.node,
175                                 defaultness,
176                                 sig.abi,
177                                 vis.unwrap_or(&ast::Visibility::Inherited),
178                                 codemap::mk_sp(s.lo, b.span.lo),
179                                 &b)
180             }
181             visit::FnKind::Closure(_) => unreachable!(),
182         };
183
184         if let Some(fn_str) = rewrite {
185             self.format_missing_with_indent(source!(self, s).lo);
186             self.buffer.push_str(&fn_str);
187             if let Some(c) = fn_str.chars().last() {
188                 if c == '}' {
189                     self.last_pos = source!(self, block.span).hi;
190                     return;
191                 }
192             }
193         } else {
194             self.format_missing(source!(self, block.span).lo);
195         }
196
197         self.last_pos = source!(self, block.span).lo;
198         self.visit_block(block)
199     }
200
201     pub fn visit_item(&mut self, item: &ast::Item) {
202         // This is where we bail out if there is a skip attribute. This is only
203         // complex in the module case. It is complex because the module could be
204         // in a separate file and there might be attributes in both files, but
205         // the AST lumps them all together.
206         match item.node {
207             ast::ItemKind::Mod(ref m) => {
208                 let outer_file = self.codemap.lookup_char_pos(item.span.lo).file;
209                 let inner_file = self.codemap.lookup_char_pos(m.inner.lo).file;
210                 if outer_file.name == inner_file.name {
211                     // Module is inline, in this case we treat modules like any
212                     // other item.
213                     if self.visit_attrs(&item.attrs) {
214                         self.push_rewrite(item.span, None);
215                         return;
216                     }
217                 } else if utils::contains_skip(&item.attrs) {
218                     // Module is not inline, but should be skipped.
219                     return;
220                 } else {
221                     // Module is not inline and should not be skipped. We want
222                     // to process only the attributes in the current file.
223                     let attrs = item.attrs
224                         .iter()
225                         .filter_map(|a| {
226                             let attr_file = self.codemap.lookup_char_pos(a.span.lo).file;
227                             if attr_file.name == outer_file.name {
228                                 Some(a.clone())
229                             } else {
230                                 None
231                             }
232                         })
233                         .collect::<Vec<_>>();
234                     // Assert because if we should skip it should be caught by
235                     // the above case.
236                     assert!(!self.visit_attrs(&attrs));
237                 }
238             }
239             _ => {
240                 if self.visit_attrs(&item.attrs) {
241                     self.push_rewrite(item.span, None);
242                     return;
243                 }
244             }
245         }
246
247         match item.node {
248             ast::ItemKind::Use(ref vp) => {
249                 self.format_import(&item.vis, vp, item.span);
250             }
251             ast::ItemKind::Impl(..) => {
252                 self.format_missing_with_indent(source!(self, item.span).lo);
253                 let snippet = self.get_context().snippet(item.span);
254                 let where_span_end =
255                     snippet
256                         .find_uncommented("{")
257                         .map(|x| (BytePos(x as u32)) + source!(self, item.span).lo);
258                 if let Some(impl_str) = format_impl(&self.get_context(),
259                                                     item,
260                                                     self.block_indent,
261                                                     where_span_end) {
262                     self.buffer.push_str(&impl_str);
263                     self.last_pos = source!(self, item.span).hi;
264                 }
265             }
266             ast::ItemKind::Trait(..) => {
267                 self.format_missing_with_indent(item.span.lo);
268                 if let Some(trait_str) = format_trait(&self.get_context(),
269                                                       item,
270                                                       self.block_indent) {
271                     self.buffer.push_str(&trait_str);
272                     self.last_pos = source!(self, item.span).hi;
273                 }
274             }
275             ast::ItemKind::ExternCrate(_) => {
276                 self.format_missing_with_indent(source!(self, item.span).lo);
277                 let new_str = self.snippet(item.span);
278                 self.buffer.push_str(&new_str);
279                 self.last_pos = source!(self, item.span).hi;
280             }
281             ast::ItemKind::Struct(ref def, ref generics) => {
282                 let rewrite = {
283                     let indent = self.block_indent;
284                     let context = self.get_context();
285                     ::items::format_struct(&context,
286                                            "struct ",
287                                            item.ident,
288                                            &item.vis,
289                                            def,
290                                            Some(generics),
291                                            item.span,
292                                            indent,
293                                            None)
294                         .map(|s| match *def {
295                                  ast::VariantData::Tuple(..) => s + ";",
296                                  _ => s,
297                              })
298                 };
299                 self.push_rewrite(item.span, rewrite);
300             }
301             ast::ItemKind::Enum(ref def, ref generics) => {
302                 self.format_missing_with_indent(source!(self, item.span).lo);
303                 self.visit_enum(item.ident, &item.vis, def, generics, item.span);
304                 self.last_pos = source!(self, item.span).hi;
305             }
306             ast::ItemKind::Mod(ref module) => {
307                 self.format_missing_with_indent(source!(self, item.span).lo);
308                 self.format_mod(module, &item.vis, item.span, item.ident);
309             }
310             ast::ItemKind::Mac(ref mac) => {
311                 self.visit_mac(mac, Some(item.ident), MacroPosition::Item);
312             }
313             ast::ItemKind::ForeignMod(ref foreign_mod) => {
314                 self.format_missing_with_indent(source!(self, item.span).lo);
315                 self.format_foreign_mod(foreign_mod, item.span);
316             }
317             ast::ItemKind::Static(ref ty, mutability, ref expr) => {
318                 let rewrite = rewrite_static("static",
319                                              &item.vis,
320                                              item.ident,
321                                              ty,
322                                              mutability,
323                                              Some(expr),
324                                              self.block_indent,
325                                              &self.get_context());
326                 self.push_rewrite(item.span, rewrite);
327             }
328             ast::ItemKind::Const(ref ty, ref expr) => {
329                 let rewrite = rewrite_static("const",
330                                              &item.vis,
331                                              item.ident,
332                                              ty,
333                                              ast::Mutability::Immutable,
334                                              Some(expr),
335                                              self.block_indent,
336                                              &self.get_context());
337                 self.push_rewrite(item.span, rewrite);
338             }
339             ast::ItemKind::DefaultImpl(..) => {
340                 // FIXME(#78): format impl definitions.
341             }
342             ast::ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
343                 self.visit_fn(visit::FnKind::ItemFn(item.ident,
344                                                     generics,
345                                                     unsafety,
346                                                     constness,
347                                                     abi,
348                                                     &item.vis,
349                                                     body),
350                               decl,
351                               item.span,
352                               item.id,
353                               ast::Defaultness::Final)
354             }
355             ast::ItemKind::Ty(ref ty, ref generics) => {
356                 let rewrite = rewrite_type_alias(&self.get_context(),
357                                                  self.block_indent,
358                                                  item.ident,
359                                                  ty,
360                                                  generics,
361                                                  &item.vis,
362                                                  item.span);
363                 self.push_rewrite(item.span, rewrite);
364             }
365             ast::ItemKind::Union(..) => {
366                 // FIXME(#1157): format union definitions.
367             }
368         }
369     }
370
371     pub fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
372         if self.visit_attrs(&ti.attrs) {
373             self.push_rewrite(ti.span, None);
374             return;
375         }
376
377         match ti.node {
378             ast::TraitItemKind::Const(ref ty, ref expr_opt) => {
379                 let rewrite = rewrite_static("const",
380                                              &ast::Visibility::Inherited,
381                                              ti.ident,
382                                              ty,
383                                              ast::Mutability::Immutable,
384                                              expr_opt.as_ref(),
385                                              self.block_indent,
386                                              &self.get_context());
387                 self.push_rewrite(ti.span, rewrite);
388             }
389             ast::TraitItemKind::Method(ref sig, None) => {
390                 let indent = self.block_indent;
391                 let rewrite = self.rewrite_required_fn(indent, ti.ident, sig, ti.span);
392                 self.push_rewrite(ti.span, rewrite);
393             }
394             ast::TraitItemKind::Method(ref sig, Some(ref body)) => {
395                 self.visit_fn(visit::FnKind::Method(ti.ident, sig, None, body),
396                               &sig.decl,
397                               ti.span,
398                               ti.id,
399                               ast::Defaultness::Final);
400             }
401             ast::TraitItemKind::Type(ref type_param_bounds, ref type_default) => {
402                 let rewrite = rewrite_associated_type(ti.ident,
403                                                       type_default.as_ref(),
404                                                       Some(type_param_bounds),
405                                                       &self.get_context(),
406                                                       self.block_indent);
407                 self.push_rewrite(ti.span, rewrite);
408             }
409             ast::TraitItemKind::Macro(ref mac) => {
410                 self.visit_mac(mac, Some(ti.ident), MacroPosition::Item);
411             }
412         }
413     }
414
415     pub fn visit_impl_item(&mut self, ii: &ast::ImplItem) {
416         if self.visit_attrs(&ii.attrs) {
417             self.push_rewrite(ii.span, None);
418             return;
419         }
420
421         match ii.node {
422             ast::ImplItemKind::Method(ref sig, ref body) => {
423                 self.visit_fn(visit::FnKind::Method(ii.ident, sig, Some(&ii.vis), body),
424                               &sig.decl,
425                               ii.span,
426                               ii.id,
427                               ii.defaultness);
428             }
429             ast::ImplItemKind::Const(ref ty, ref expr) => {
430                 let rewrite = rewrite_static("const",
431                                              &ii.vis,
432                                              ii.ident,
433                                              ty,
434                                              ast::Mutability::Immutable,
435                                              Some(expr),
436                                              self.block_indent,
437                                              &self.get_context());
438                 self.push_rewrite(ii.span, rewrite);
439             }
440             ast::ImplItemKind::Type(ref ty) => {
441                 let rewrite = rewrite_associated_impl_type(ii.ident,
442                                                            ii.defaultness,
443                                                            Some(ty),
444                                                            None,
445                                                            &self.get_context(),
446                                                            self.block_indent);
447                 self.push_rewrite(ii.span, rewrite);
448             }
449             ast::ImplItemKind::Macro(ref mac) => {
450                 self.visit_mac(mac, Some(ii.ident), MacroPosition::Item);
451             }
452         }
453     }
454
455     fn visit_mac(&mut self, mac: &ast::Mac, ident: Option<ast::Ident>, pos: MacroPosition) {
456         // 1 = ;
457         let shape = Shape::indented(self.block_indent, self.config)
458             .sub_width(1)
459             .unwrap();
460         let rewrite = rewrite_macro(mac, ident, &self.get_context(), shape, pos);
461         self.push_rewrite(mac.span, rewrite);
462     }
463
464     fn push_rewrite(&mut self, span: Span, rewrite: Option<String>) {
465         self.format_missing_with_indent(source!(self, span).lo);
466         self.failed = match rewrite {
467             Some(ref s) if s.rewrite(&self.get_context(),
468                                      Shape::indented(self.block_indent, self.config))
469                                .is_none() => true,
470             None => true,
471             _ => self.failed,
472         };
473         let result = rewrite.unwrap_or_else(|| self.snippet(span));
474         self.buffer.push_str(&result);
475         self.last_pos = source!(self, span).hi;
476     }
477
478     pub fn from_codemap(parse_session: &'a ParseSess, config: &'a Config) -> FmtVisitor<'a> {
479         FmtVisitor {
480             parse_session: parse_session,
481             codemap: parse_session.codemap(),
482             buffer: StringBuffer::new(),
483             last_pos: BytePos(0),
484             block_indent: Indent::empty(),
485             config: config,
486             failed: false,
487         }
488     }
489
490     pub fn snippet(&self, span: Span) -> String {
491         match self.codemap.span_to_snippet(span) {
492             Ok(s) => s,
493             Err(_) => {
494                 println!("Couldn't make snippet for span {:?}->{:?}",
495                          self.codemap.lookup_char_pos(span.lo),
496                          self.codemap.lookup_char_pos(span.hi));
497                 "".to_owned()
498             }
499         }
500     }
501
502     // Returns true if we should skip the following item.
503     pub fn visit_attrs(&mut self, attrs: &[ast::Attribute]) -> bool {
504         if utils::contains_skip(attrs) {
505             return true;
506         }
507
508         let outers: Vec<_> = attrs
509             .iter()
510             .filter(|a| a.style == ast::AttrStyle::Outer)
511             .cloned()
512             .collect();
513         if outers.is_empty() {
514             return false;
515         }
516
517         let first = &outers[0];
518         self.format_missing_with_indent(source!(self, first.span).lo);
519
520         let rewrite = outers
521             .rewrite(&self.get_context(),
522                      Shape::indented(self.block_indent, self.config))
523             .unwrap();
524         self.buffer.push_str(&rewrite);
525         let last = outers.last().unwrap();
526         self.last_pos = source!(self, last.span).hi;
527         false
528     }
529
530     fn walk_mod_items(&mut self, m: &ast::Mod) {
531         let mut items_left: &[ptr::P<ast::Item>] = &m.items;
532         while !items_left.is_empty() {
533             // If the next item is a `use` declaration, then extract it and any subsequent `use`s
534             // to be potentially reordered within `format_imports`. Otherwise, just format the
535             // next item for output.
536             if self.config.reorder_imports() && is_use_item(&*items_left[0]) {
537                 let reorder_imports_in_group = self.config.reorder_imports_in_group();
538                 let mut last = self.codemap.lookup_line_range(item_bound(&items_left[0]));
539                 let use_item_length = items_left
540                     .iter()
541                     .take_while(|ppi| {
542                         is_use_item(&***ppi) &&
543                         (!reorder_imports_in_group ||
544                          {
545                              let current = self.codemap.lookup_line_range(item_bound(&ppi));
546                              let in_same_group = current.lo < last.hi + 2;
547                              last = current;
548                              in_same_group
549                          })
550                     })
551                     .count();
552                 let (use_items, rest) = items_left.split_at(use_item_length);
553                 self.format_imports(use_items);
554                 items_left = rest;
555             } else {
556                 // `unwrap()` is safe here because we know `items_left`
557                 // has elements from the loop condition
558                 let (item, rest) = items_left.split_first().unwrap();
559                 self.visit_item(item);
560                 items_left = rest;
561             }
562         }
563     }
564
565     fn format_mod(&mut self, m: &ast::Mod, vis: &ast::Visibility, s: Span, ident: ast::Ident) {
566         // Decide whether this is an inline mod or an external mod.
567         let local_file_name = self.codemap.span_to_filename(s);
568         let inner_span = source!(self, m.inner);
569         let is_internal = !(inner_span.lo.0 == 0 && inner_span.hi.0 == 0) &&
570                           local_file_name == self.codemap.span_to_filename(inner_span);
571
572         self.buffer.push_str(&*utils::format_visibility(vis));
573         self.buffer.push_str("mod ");
574         self.buffer.push_str(&ident.to_string());
575
576         if is_internal {
577             self.buffer.push_str(" {");
578             // Hackery to account for the closing }.
579             let mod_lo = self.codemap.span_after(source!(self, s), "{");
580             let body_snippet =
581                 self.snippet(codemap::mk_sp(mod_lo, source!(self, m.inner).hi - BytePos(1)));
582             let body_snippet = body_snippet.trim();
583             if body_snippet.is_empty() {
584                 self.buffer.push_str("}");
585             } else {
586                 self.last_pos = mod_lo;
587                 self.block_indent = self.block_indent.block_indent(self.config);
588                 self.walk_mod_items(m);
589                 self.format_missing_with_indent(source!(self, m.inner).hi - BytePos(1));
590                 self.close_block();
591             }
592             self.last_pos = source!(self, m.inner).hi;
593         } else {
594             self.buffer.push_str(";");
595             self.last_pos = source!(self, s).hi;
596         }
597     }
598
599     pub fn format_separate_mod(&mut self, m: &ast::Mod) {
600         let filemap = self.codemap.lookup_char_pos(m.inner.lo).file;
601         self.last_pos = filemap.start_pos;
602         self.block_indent = Indent::empty();
603         self.walk_mod_items(m);
604         self.format_missing_with_indent(filemap.end_pos);
605     }
606
607     pub fn get_context(&self) -> RewriteContext {
608         RewriteContext {
609             parse_session: self.parse_session,
610             codemap: self.codemap,
611             config: self.config,
612             inside_macro: false,
613         }
614     }
615 }
616
617 impl<'a> Rewrite for [ast::Attribute] {
618     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
619         let mut result = String::new();
620         if self.is_empty() {
621             return Some(result);
622         }
623         let indent = shape.indent.to_string(context.config);
624
625         for (i, a) in self.iter().enumerate() {
626             let mut a_str = context.snippet(a.span);
627
628             // Write comments and blank lines between attributes.
629             if i > 0 {
630                 let comment = context.snippet(codemap::mk_sp(self[i - 1].span.hi, a.span.lo));
631                 // This particular horror show is to preserve line breaks in between doc
632                 // comments. An alternative would be to force such line breaks to start
633                 // with the usual doc comment token.
634                 let multi_line = a_str.starts_with("//") && comment.matches('\n').count() > 1;
635                 let comment = comment.trim();
636                 if !comment.is_empty() {
637                     let comment =
638                         try_opt!(rewrite_comment(comment,
639                                                  false,
640                                                  Shape::legacy(context.config.comment_width() -
641                                                                shape.indent.width(),
642                                                                shape.indent),
643                                                  context.config));
644                     result.push_str(&indent);
645                     result.push_str(&comment);
646                     result.push('\n');
647                 } else if multi_line {
648                     result.push('\n');
649                 }
650                 result.push_str(&indent);
651             }
652
653             if a_str.starts_with("//") {
654                 a_str = try_opt!(rewrite_comment(&a_str,
655                                                  false,
656                                                  Shape::legacy(context.config.comment_width() -
657                                                                shape.indent.width(),
658                                                                shape.indent),
659                                                  context.config));
660             }
661
662             // Write the attribute itself.
663             result.push_str(&a_str);
664
665             if i < self.len() - 1 {
666                 result.push('\n');
667             }
668         }
669
670         Some(result)
671     }
672 }