]> git.lizzy.rs Git - rust.git/blob - src/visitor.rs
Use push_rewrite() to remove duplicates
[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 strings::string_buffer::StringBuffer;
14 use syntax::{ast, ptr, visit};
15 use syntax::attr::HasAttrs;
16 use syntax::codemap::{self, BytePos, CodeMap, Pos, Span};
17 use syntax::parse::ParseSess;
18
19 use {Indent, Shape, Spanned};
20 use codemap::{LineRangeUtils, SpanUtils};
21 use comment::{contains_comment, recover_missing_comment_in_span, CodeCharKind, CommentCodeSlices,
22               FindUncommented};
23 use comment::rewrite_comment;
24 use config::{BraceStyle, Config};
25 use items::{format_impl, format_trait, rewrite_associated_impl_type, rewrite_associated_type,
26             rewrite_static, rewrite_type_alias};
27 use lists::{itemize_list, write_list, DefinitiveListTactic, ListFormatting, SeparatorPlace,
28             SeparatorTactic};
29 use macros::{rewrite_macro, MacroPosition};
30 use regex::Regex;
31 use rewrite::{Rewrite, RewriteContext};
32 use utils::{self, contains_skip, inner_attributes, mk_sp};
33
34 fn is_use_item(item: &ast::Item) -> bool {
35     match item.node {
36         ast::ItemKind::Use(_) => true,
37         _ => false,
38     }
39 }
40
41 fn is_extern_crate(item: &ast::Item) -> bool {
42     match item.node {
43         ast::ItemKind::ExternCrate(..) => true,
44         _ => false,
45     }
46 }
47
48 pub struct FmtVisitor<'a> {
49     pub parse_session: &'a ParseSess,
50     pub codemap: &'a CodeMap,
51     pub buffer: StringBuffer,
52     pub last_pos: BytePos,
53     // FIXME: use an RAII util or closure for indenting
54     pub block_indent: Indent,
55     pub config: &'a Config,
56     pub is_if_else_block: bool,
57 }
58
59 impl<'a> FmtVisitor<'a> {
60     pub fn shape(&self) -> Shape {
61         Shape::indented(self.block_indent, self.config)
62     }
63
64     fn visit_stmt(&mut self, stmt: &ast::Stmt) {
65         debug!(
66             "visit_stmt: {:?} {:?}",
67             self.codemap.lookup_char_pos(stmt.span.lo()),
68             self.codemap.lookup_char_pos(stmt.span.hi())
69         );
70
71         match stmt.node {
72             ast::StmtKind::Item(ref item) => {
73                 self.visit_item(item);
74             }
75             ast::StmtKind::Local(..) => {
76                 let rewrite = stmt.rewrite(&self.get_context(), self.shape());
77                 self.push_rewrite(stmt.span(), rewrite);
78             }
79             ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => {
80                 let rewrite = stmt.rewrite(&self.get_context(), self.shape());
81                 self.push_rewrite(stmt.span(), rewrite)
82             }
83             ast::StmtKind::Mac(ref mac) => {
84                 let (ref mac, _macro_style, ref attrs) = **mac;
85                 if self.visit_attrs(attrs, ast::AttrStyle::Outer) {
86                     self.push_rewrite(stmt.span(), None);
87                 } else {
88                     self.visit_mac(mac, None, MacroPosition::Statement);
89                 }
90                 self.format_missing(stmt.span.hi());
91             }
92         }
93     }
94
95     pub fn visit_block(&mut self, b: &ast::Block, inner_attrs: Option<&[ast::Attribute]>) {
96         debug!(
97             "visit_block: {:?} {:?}",
98             self.codemap.lookup_char_pos(b.span.lo()),
99             self.codemap.lookup_char_pos(b.span.hi())
100         );
101
102         // Check if this block has braces.
103         let snippet = self.snippet(b.span);
104         let has_braces = snippet.starts_with('{') || snippet.starts_with("unsafe");
105         let brace_compensation = if has_braces { BytePos(1) } else { BytePos(0) };
106
107         self.last_pos = self.last_pos + brace_compensation;
108         self.block_indent = self.block_indent.block_indent(self.config);
109         self.buffer.push_str("{");
110
111         if self.config.remove_blank_lines_at_start_or_end_of_block() {
112             if let Some(first_stmt) = b.stmts.first() {
113                 let attr_lo = inner_attrs
114                     .and_then(|attrs| {
115                         inner_attributes(attrs).first().map(|attr| attr.span.lo())
116                     })
117                     .or_else(|| {
118                         // Attributes for an item in a statement position
119                         // do not belong to the statement. (rust-lang/rust#34459)
120                         if let ast::StmtKind::Item(ref item) = first_stmt.node {
121                             item.attrs.first()
122                         } else {
123                             first_stmt.attrs().first()
124                         }.and_then(|attr| {
125                             // Some stmts can have embedded attributes.
126                             // e.g. `match { #![attr] ... }`
127                             let attr_lo = attr.span.lo();
128                             if attr_lo < first_stmt.span.lo() {
129                                 Some(attr_lo)
130                             } else {
131                                 None
132                             }
133                         })
134                     });
135
136                 let snippet = self.snippet(mk_sp(
137                     self.last_pos,
138                     attr_lo.unwrap_or(first_stmt.span.lo()),
139                 ));
140                 let len = CommentCodeSlices::new(&snippet).nth(0).and_then(
141                     |(kind, _, s)| if kind == CodeCharKind::Normal {
142                         s.rfind('\n')
143                     } else {
144                         None
145                     },
146                 );
147                 if let Some(len) = len {
148                     self.last_pos = self.last_pos + BytePos::from_usize(len);
149                 }
150             }
151         }
152
153         // Format inner attributes if available.
154         if let Some(attrs) = inner_attrs {
155             self.visit_attrs(attrs, ast::AttrStyle::Inner);
156         }
157
158         for stmt in &b.stmts {
159             self.visit_stmt(stmt)
160         }
161
162         if !b.stmts.is_empty() {
163             if let Some(expr) = utils::stmt_expr(&b.stmts[b.stmts.len() - 1]) {
164                 if utils::semicolon_for_expr(&self.get_context(), expr) {
165                     self.buffer.push_str(";");
166                 }
167             }
168         }
169
170         let mut remove_len = BytePos(0);
171         if self.config.remove_blank_lines_at_start_or_end_of_block() {
172             if let Some(stmt) = b.stmts.last() {
173                 let snippet = self.snippet(mk_sp(
174                     stmt.span.hi(),
175                     source!(self, b.span).hi() - brace_compensation,
176                 ));
177                 let len = CommentCodeSlices::new(&snippet)
178                     .last()
179                     .and_then(|(kind, _, s)| {
180                         if kind == CodeCharKind::Normal && s.trim().is_empty() {
181                             Some(s.len())
182                         } else {
183                             None
184                         }
185                     });
186                 if let Some(len) = len {
187                     remove_len = BytePos::from_usize(len);
188                 }
189             }
190         }
191
192         let mut unindent_comment = self.is_if_else_block && !b.stmts.is_empty();
193         if unindent_comment {
194             let end_pos = source!(self, b.span).hi() - brace_compensation - remove_len;
195             let snippet = self.snippet(mk_sp(self.last_pos, end_pos));
196             unindent_comment = snippet.contains("//") || snippet.contains("/*");
197         }
198         // FIXME: we should compress any newlines here to just one
199         if unindent_comment {
200             self.block_indent = self.block_indent.block_unindent(self.config);
201         }
202         self.format_missing_with_indent(
203             source!(self, b.span).hi() - brace_compensation - remove_len,
204         );
205         if unindent_comment {
206             self.block_indent = self.block_indent.block_indent(self.config);
207         }
208         self.close_block(unindent_comment);
209         self.last_pos = source!(self, b.span).hi();
210     }
211
212     // FIXME: this is a terrible hack to indent the comments between the last
213     // item in the block and the closing brace to the block's level.
214     // The closing brace itself, however, should be indented at a shallower
215     // level.
216     fn close_block(&mut self, unindent_comment: bool) {
217         let total_len = self.buffer.len;
218         let chars_too_many = if unindent_comment {
219             0
220         } else if self.config.hard_tabs() {
221             1
222         } else {
223             self.config.tab_spaces()
224         };
225         self.buffer.truncate(total_len - chars_too_many);
226         self.buffer.push_str("}");
227         self.block_indent = self.block_indent.block_unindent(self.config);
228     }
229
230     // Note that this only gets called for function definitions. Required methods
231     // on traits do not get handled here.
232     fn visit_fn(
233         &mut self,
234         fk: visit::FnKind,
235         fd: &ast::FnDecl,
236         s: Span,
237         _: ast::NodeId,
238         defaultness: ast::Defaultness,
239         inner_attrs: Option<&[ast::Attribute]>,
240     ) {
241         let indent = self.block_indent;
242         let block;
243         let rewrite = match fk {
244             visit::FnKind::ItemFn(ident, generics, unsafety, constness, abi, vis, b) => {
245                 block = b;
246                 self.rewrite_fn(
247                     indent,
248                     ident,
249                     fd,
250                     generics,
251                     unsafety,
252                     constness.node,
253                     defaultness,
254                     abi,
255                     vis,
256                     mk_sp(s.lo(), b.span.lo()),
257                     b,
258                 )
259             }
260             visit::FnKind::Method(ident, sig, vis, b) => {
261                 block = b;
262                 self.rewrite_fn(
263                     indent,
264                     ident,
265                     fd,
266                     &sig.generics,
267                     sig.unsafety,
268                     sig.constness.node,
269                     defaultness,
270                     sig.abi,
271                     vis.unwrap_or(&ast::Visibility::Inherited),
272                     mk_sp(s.lo(), b.span.lo()),
273                     b,
274                 )
275             }
276             visit::FnKind::Closure(_) => unreachable!(),
277         };
278
279         if let Some(fn_str) = rewrite {
280             self.format_missing_with_indent(source!(self, s).lo());
281             self.buffer.push_str(&fn_str);
282             if let Some(c) = fn_str.chars().last() {
283                 if c == '}' {
284                     self.last_pos = source!(self, block.span).hi();
285                     return;
286                 }
287             }
288         } else {
289             self.format_missing(source!(self, block.span).lo());
290         }
291
292         self.last_pos = source!(self, block.span).lo();
293         self.visit_block(block, inner_attrs)
294     }
295
296     pub fn visit_item(&mut self, item: &ast::Item) {
297         skip_out_of_file_lines_range_visitor!(self, item.span);
298
299         // This is where we bail out if there is a skip attribute. This is only
300         // complex in the module case. It is complex because the module could be
301         // in a separate file and there might be attributes in both files, but
302         // the AST lumps them all together.
303         let filterd_attrs;
304         let mut attrs = &item.attrs;
305         match item.node {
306             ast::ItemKind::Mod(ref m) => {
307                 let outer_file = self.codemap.lookup_char_pos(item.span.lo()).file;
308                 let inner_file = self.codemap.lookup_char_pos(m.inner.lo()).file;
309                 if outer_file.name == inner_file.name {
310                     // Module is inline, in this case we treat modules like any
311                     // other item.
312                     if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
313                         self.push_rewrite(item.span, None);
314                         return;
315                     }
316                 } else if contains_skip(&item.attrs) {
317                     // Module is not inline, but should be skipped.
318                     return;
319                 } else {
320                     // Module is not inline and should not be skipped. We want
321                     // to process only the attributes in the current file.
322                     filterd_attrs = item.attrs
323                         .iter()
324                         .filter_map(|a| {
325                             let attr_file = self.codemap.lookup_char_pos(a.span.lo()).file;
326                             if attr_file.name == outer_file.name {
327                                 Some(a.clone())
328                             } else {
329                                 None
330                             }
331                         })
332                         .collect::<Vec<_>>();
333                     // Assert because if we should skip it should be caught by
334                     // the above case.
335                     assert!(!self.visit_attrs(&filterd_attrs, ast::AttrStyle::Outer));
336                     attrs = &filterd_attrs;
337                 }
338             }
339             _ => if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) {
340                 self.push_rewrite(item.span, None);
341                 return;
342             },
343         }
344
345         match item.node {
346             ast::ItemKind::Use(ref vp) => self.format_import(&item, vp),
347             ast::ItemKind::Impl(..) => {
348                 let snippet = self.snippet(item.span);
349                 let where_span_end = snippet
350                     .find_uncommented("{")
351                     .map(|x| (BytePos(x as u32)) + source!(self, item.span).lo());
352                 let rw = format_impl(&self.get_context(), item, self.block_indent, where_span_end);
353                 self.push_rewrite(item.span, rw);
354             }
355             ast::ItemKind::Trait(..) => {
356                 let rw = format_trait(&self.get_context(), item, self.block_indent);
357                 self.push_rewrite(item.span, rw);
358             }
359             ast::ItemKind::ExternCrate(_) => {
360                 let rw = rewrite_extern_crate(&self.get_context(), item);
361                 self.push_rewrite(item.span, rw);
362             }
363             ast::ItemKind::Struct(ref def, ref generics) => {
364                 let rewrite = {
365                     ::items::format_struct(
366                         &self.get_context(),
367                         "struct ",
368                         item.ident,
369                         &item.vis,
370                         def,
371                         Some(generics),
372                         item.span,
373                         self.block_indent,
374                         None,
375                     ).map(|s| match *def {
376                         ast::VariantData::Tuple(..) => s + ";",
377                         _ => s,
378                     })
379                 };
380                 self.push_rewrite(item.span, rewrite);
381             }
382             ast::ItemKind::Enum(ref def, ref generics) => {
383                 self.format_missing_with_indent(source!(self, item.span).lo());
384                 self.visit_enum(item.ident, &item.vis, def, generics, item.span);
385                 self.last_pos = source!(self, item.span).hi();
386             }
387             ast::ItemKind::Mod(ref module) => {
388                 self.format_missing_with_indent(source!(self, item.span).lo());
389                 self.format_mod(module, &item.vis, item.span, item.ident, &attrs);
390             }
391             ast::ItemKind::Mac(ref mac) => {
392                 self.visit_mac(mac, Some(item.ident), MacroPosition::Item);
393             }
394             ast::ItemKind::ForeignMod(ref foreign_mod) => {
395                 self.format_missing_with_indent(source!(self, item.span).lo());
396                 self.format_foreign_mod(foreign_mod, item.span);
397             }
398             ast::ItemKind::Static(ref ty, mutability, ref expr) => {
399                 let rewrite = rewrite_static(
400                     "static",
401                     &item.vis,
402                     item.ident,
403                     ty,
404                     mutability,
405                     Some(expr),
406                     self.block_indent,
407                     item.span,
408                     &self.get_context(),
409                 );
410                 self.push_rewrite(item.span, rewrite);
411             }
412             ast::ItemKind::Const(ref ty, ref expr) => {
413                 let rewrite = rewrite_static(
414                     "const",
415                     &item.vis,
416                     item.ident,
417                     ty,
418                     ast::Mutability::Immutable,
419                     Some(expr),
420                     self.block_indent,
421                     item.span,
422                     &self.get_context(),
423                 );
424                 self.push_rewrite(item.span, rewrite);
425             }
426             ast::ItemKind::DefaultImpl(..) => {
427                 // FIXME(#78): format impl definitions.
428             }
429             ast::ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
430                 self.visit_fn(
431                     visit::FnKind::ItemFn(
432                         item.ident,
433                         generics,
434                         unsafety,
435                         constness,
436                         abi,
437                         &item.vis,
438                         body,
439                     ),
440                     decl,
441                     item.span,
442                     item.id,
443                     ast::Defaultness::Final,
444                     Some(&item.attrs),
445                 )
446             }
447             ast::ItemKind::Ty(ref ty, ref generics) => {
448                 let rewrite = rewrite_type_alias(
449                     &self.get_context(),
450                     self.block_indent,
451                     item.ident,
452                     ty,
453                     generics,
454                     &item.vis,
455                     item.span,
456                 );
457                 self.push_rewrite(item.span, rewrite);
458             }
459             ast::ItemKind::Union(ref def, ref generics) => {
460                 let rewrite = ::items::format_struct_struct(
461                     &self.get_context(),
462                     "union ",
463                     item.ident,
464                     &item.vis,
465                     def.fields(),
466                     Some(generics),
467                     item.span,
468                     self.block_indent,
469                     None,
470                 );
471                 self.push_rewrite(item.span, rewrite);
472             }
473             ast::ItemKind::GlobalAsm(..) => {
474                 let snippet = Some(self.snippet(item.span));
475                 self.push_rewrite(item.span, snippet);
476             }
477             ast::ItemKind::MacroDef(..) => {
478                 // FIXME(#1539): macros 2.0
479                 let mac_snippet = Some(self.snippet(item.span));
480                 self.push_rewrite(item.span, mac_snippet);
481             }
482         }
483     }
484
485     pub fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
486         skip_out_of_file_lines_range_visitor!(self, ti.span);
487
488         if self.visit_attrs(&ti.attrs, ast::AttrStyle::Outer) {
489             self.push_rewrite(ti.span, None);
490             return;
491         }
492
493         match ti.node {
494             ast::TraitItemKind::Const(ref ty, ref expr_opt) => {
495                 let rewrite = rewrite_static(
496                     "const",
497                     &ast::Visibility::Inherited,
498                     ti.ident,
499                     ty,
500                     ast::Mutability::Immutable,
501                     expr_opt.as_ref(),
502                     self.block_indent,
503                     ti.span,
504                     &self.get_context(),
505                 );
506                 self.push_rewrite(ti.span, rewrite);
507             }
508             ast::TraitItemKind::Method(ref sig, None) => {
509                 let indent = self.block_indent;
510                 let rewrite = self.rewrite_required_fn(indent, ti.ident, sig, ti.span);
511                 self.push_rewrite(ti.span, rewrite);
512             }
513             ast::TraitItemKind::Method(ref sig, Some(ref body)) => {
514                 self.visit_fn(
515                     visit::FnKind::Method(ti.ident, sig, None, body),
516                     &sig.decl,
517                     ti.span,
518                     ti.id,
519                     ast::Defaultness::Final,
520                     Some(&ti.attrs),
521                 );
522             }
523             ast::TraitItemKind::Type(ref type_param_bounds, ref type_default) => {
524                 let rewrite = rewrite_associated_type(
525                     ti.ident,
526                     type_default.as_ref(),
527                     Some(type_param_bounds),
528                     &self.get_context(),
529                     self.block_indent,
530                 );
531                 self.push_rewrite(ti.span, rewrite);
532             }
533             ast::TraitItemKind::Macro(ref mac) => {
534                 self.visit_mac(mac, Some(ti.ident), MacroPosition::Item);
535             }
536         }
537     }
538
539     pub fn visit_impl_item(&mut self, ii: &ast::ImplItem) {
540         skip_out_of_file_lines_range_visitor!(self, ii.span);
541
542         if self.visit_attrs(&ii.attrs, ast::AttrStyle::Outer) {
543             self.push_rewrite(ii.span, None);
544             return;
545         }
546
547         match ii.node {
548             ast::ImplItemKind::Method(ref sig, ref body) => {
549                 self.visit_fn(
550                     visit::FnKind::Method(ii.ident, sig, Some(&ii.vis), body),
551                     &sig.decl,
552                     ii.span,
553                     ii.id,
554                     ii.defaultness,
555                     Some(&ii.attrs),
556                 );
557             }
558             ast::ImplItemKind::Const(ref ty, ref expr) => {
559                 let rewrite = rewrite_static(
560                     "const",
561                     &ii.vis,
562                     ii.ident,
563                     ty,
564                     ast::Mutability::Immutable,
565                     Some(expr),
566                     self.block_indent,
567                     ii.span,
568                     &self.get_context(),
569                 );
570                 self.push_rewrite(ii.span, rewrite);
571             }
572             ast::ImplItemKind::Type(ref ty) => {
573                 let rewrite = rewrite_associated_impl_type(
574                     ii.ident,
575                     ii.defaultness,
576                     Some(ty),
577                     None,
578                     &self.get_context(),
579                     self.block_indent,
580                 );
581                 self.push_rewrite(ii.span, rewrite);
582             }
583             ast::ImplItemKind::Macro(ref mac) => {
584                 self.visit_mac(mac, Some(ii.ident), MacroPosition::Item);
585             }
586         }
587     }
588
589     fn visit_mac(&mut self, mac: &ast::Mac, ident: Option<ast::Ident>, pos: MacroPosition) {
590         skip_out_of_file_lines_range_visitor!(self, mac.span);
591
592         // 1 = ;
593         let shape = self.shape().sub_width(1).unwrap();
594         let rewrite = rewrite_macro(mac, ident, &self.get_context(), shape, pos);
595         self.push_rewrite(mac.span, rewrite);
596     }
597
598     pub fn push_rewrite(&mut self, span: Span, rewrite: Option<String>) {
599         self.format_missing_with_indent(source!(self, span).lo());
600         let result = rewrite.unwrap_or_else(|| self.snippet(span));
601         self.buffer.push_str(&result);
602         self.last_pos = source!(self, span).hi();
603     }
604
605     pub fn from_codemap(parse_session: &'a ParseSess, config: &'a Config) -> FmtVisitor<'a> {
606         FmtVisitor {
607             parse_session: parse_session,
608             codemap: parse_session.codemap(),
609             buffer: StringBuffer::new(),
610             last_pos: BytePos(0),
611             block_indent: Indent::empty(),
612             config: config,
613             is_if_else_block: false,
614         }
615     }
616
617     pub fn snippet(&self, span: Span) -> String {
618         match self.codemap.span_to_snippet(span) {
619             Ok(s) => s,
620             Err(_) => {
621                 println!(
622                     "Couldn't make snippet for span {:?}->{:?}",
623                     self.codemap.lookup_char_pos(span.lo()),
624                     self.codemap.lookup_char_pos(span.hi())
625                 );
626                 "".to_owned()
627             }
628         }
629     }
630
631     // Returns true if we should skip the following item.
632     pub fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool {
633         if contains_skip(attrs) {
634             return true;
635         }
636
637         let attrs: Vec<_> = attrs.iter().filter(|a| a.style == style).cloned().collect();
638         if attrs.is_empty() {
639             return false;
640         }
641
642         let rewrite = attrs.rewrite(&self.get_context(), self.shape());
643         let span = mk_sp(attrs[0].span.lo(), attrs[attrs.len() - 1].span.hi());
644         self.push_rewrite(span, rewrite);
645
646         false
647     }
648
649     fn reorder_items<F>(
650         &mut self,
651         items_left: &[ptr::P<ast::Item>],
652         is_item: &F,
653         in_group: bool,
654     ) -> usize
655     where
656         F: Fn(&ast::Item) -> bool,
657     {
658         let mut last = self.codemap.lookup_line_range(items_left[0].span());
659         let item_length = items_left
660             .iter()
661             .take_while(|ppi| {
662                 is_item(&***ppi) && (!in_group || {
663                     let current = self.codemap.lookup_line_range(ppi.span());
664                     let in_same_group = current.lo < last.hi + 2;
665                     last = current;
666                     in_same_group
667                 })
668             })
669             .count();
670         let items = &items_left[..item_length];
671
672         let at_least_one_in_file_lines = items
673             .iter()
674             .any(|item| !out_of_file_lines_range!(self, item.span));
675
676         if at_least_one_in_file_lines {
677             self.format_imports(items);
678         } else {
679             for item in items {
680                 self.push_rewrite(item.span, None);
681             }
682         }
683
684         item_length
685     }
686
687     fn walk_mod_items(&mut self, m: &ast::Mod) {
688         let mut items_left: &[ptr::P<ast::Item>] = &m.items;
689         while !items_left.is_empty() {
690             // If the next item is a `use` declaration, then extract it and any subsequent `use`s
691             // to be potentially reordered within `format_imports`. Otherwise, just format the
692             // next item for output.
693             if self.config.reorder_imports() && is_use_item(&*items_left[0]) {
694                 let used_items_len = self.reorder_items(
695                     items_left,
696                     &is_use_item,
697                     self.config.reorder_imports_in_group(),
698                 );
699                 let (_, rest) = items_left.split_at(used_items_len);
700                 items_left = rest;
701             } else if self.config.reorder_extern_crates() && is_extern_crate(&*items_left[0]) {
702                 let used_items_len = self.reorder_items(
703                     items_left,
704                     &is_extern_crate,
705                     self.config.reorder_extern_crates_in_group(),
706                 );
707                 let (_, rest) = items_left.split_at(used_items_len);
708                 items_left = rest;
709             } else {
710                 // `unwrap()` is safe here because we know `items_left`
711                 // has elements from the loop condition
712                 let (item, rest) = items_left.split_first().unwrap();
713                 self.visit_item(item);
714                 items_left = rest;
715             }
716         }
717     }
718
719     fn format_mod(
720         &mut self,
721         m: &ast::Mod,
722         vis: &ast::Visibility,
723         s: Span,
724         ident: ast::Ident,
725         attrs: &[ast::Attribute],
726     ) {
727         // Decide whether this is an inline mod or an external mod.
728         let local_file_name = self.codemap.span_to_filename(s);
729         let inner_span = source!(self, m.inner);
730         let is_internal = !(inner_span.lo().0 == 0 && inner_span.hi().0 == 0) &&
731             local_file_name == self.codemap.span_to_filename(inner_span);
732
733         self.buffer.push_str(&*utils::format_visibility(vis));
734         self.buffer.push_str("mod ");
735         self.buffer.push_str(&ident.to_string());
736
737         if is_internal {
738             match self.config.item_brace_style() {
739                 BraceStyle::AlwaysNextLine => self.buffer
740                     .push_str(&format!("\n{}{{", self.block_indent.to_string(self.config))),
741                 _ => self.buffer.push_str(" {"),
742             }
743             // Hackery to account for the closing }.
744             let mod_lo = self.codemap.span_after(source!(self, s), "{");
745             let body_snippet =
746                 self.snippet(mk_sp(mod_lo, source!(self, m.inner).hi() - BytePos(1)));
747             let body_snippet = body_snippet.trim();
748             if body_snippet.is_empty() {
749                 self.buffer.push_str("}");
750             } else {
751                 self.last_pos = mod_lo;
752                 self.block_indent = self.block_indent.block_indent(self.config);
753                 self.visit_attrs(attrs, ast::AttrStyle::Inner);
754                 self.walk_mod_items(m);
755                 self.format_missing_with_indent(source!(self, m.inner).hi() - BytePos(1));
756                 self.close_block(false);
757             }
758             self.last_pos = source!(self, m.inner).hi();
759         } else {
760             self.buffer.push_str(";");
761             self.last_pos = source!(self, s).hi();
762         }
763     }
764
765     pub fn format_separate_mod(&mut self, m: &ast::Mod, filemap: &codemap::FileMap) {
766         self.block_indent = Indent::empty();
767         self.walk_mod_items(m);
768         self.format_missing_with_indent(filemap.end_pos);
769     }
770
771     pub fn get_context(&self) -> RewriteContext {
772         RewriteContext {
773             parse_session: self.parse_session,
774             codemap: self.codemap,
775             config: self.config,
776             inside_macro: false,
777             use_block: false,
778             is_if_else_block: false,
779             force_one_line_chain: false,
780         }
781     }
782 }
783
784 impl Rewrite for ast::NestedMetaItem {
785     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
786         match self.node {
787             ast::NestedMetaItemKind::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
788             ast::NestedMetaItemKind::Literal(..) => Some(context.snippet(self.span)),
789         }
790     }
791 }
792
793 impl Rewrite for ast::MetaItem {
794     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
795         Some(match self.node {
796             ast::MetaItemKind::Word => String::from(&*self.name.as_str()),
797             ast::MetaItemKind::List(ref list) => {
798                 let name = self.name.as_str();
799                 // 3 = `#[` and `(`, 2 = `]` and `)`
800                 let item_shape = try_opt!(
801                     shape
802                         .shrink_left(name.len() + 3)
803                         .and_then(|s| s.sub_width(2))
804                 );
805                 let items = itemize_list(
806                     context.codemap,
807                     list.iter(),
808                     ")",
809                     |nested_meta_item| nested_meta_item.span.lo(),
810                     |nested_meta_item| nested_meta_item.span.hi(),
811                     |nested_meta_item| nested_meta_item.rewrite(context, item_shape),
812                     self.span.lo(),
813                     self.span.hi(),
814                     false,
815                 );
816                 let item_vec = items.collect::<Vec<_>>();
817                 let fmt = ListFormatting {
818                     tactic: DefinitiveListTactic::Mixed,
819                     separator: ",",
820                     trailing_separator: SeparatorTactic::Never,
821                     separator_place: SeparatorPlace::Back,
822                     shape: item_shape,
823                     ends_with_newline: false,
824                     preserve_newline: false,
825                     config: context.config,
826                 };
827                 format!("{}({})", name, try_opt!(write_list(&item_vec, &fmt)))
828             }
829             ast::MetaItemKind::NameValue(ref literal) => {
830                 let name = self.name.as_str();
831                 let value = context.snippet(literal.span);
832                 if &*name == "doc" && contains_comment(&value) {
833                     let doc_shape = Shape {
834                         width: cmp::min(shape.width, context.config.comment_width())
835                             .checked_sub(shape.indent.width())
836                             .unwrap_or(0),
837                         ..shape
838                     };
839                     try_opt!(rewrite_comment(&value, false, doc_shape, context.config))
840                 } else {
841                     format!("{} = {}", name, value)
842                 }
843             }
844         })
845     }
846 }
847
848 impl Rewrite for ast::Attribute {
849     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
850         try_opt!(self.meta())
851             .rewrite(context, shape)
852             .map(|rw| if self.is_sugared_doc {
853                 rw
854             } else {
855                 let original = context.snippet(self.span);
856                 let prefix = match self.style {
857                     ast::AttrStyle::Inner => "#!",
858                     ast::AttrStyle::Outer => "#",
859                 };
860                 if contains_comment(&original) {
861                     original
862                 } else {
863                     format!("{}[{}]", prefix, rw)
864                 }
865             })
866     }
867 }
868
869 impl<'a> Rewrite for [ast::Attribute] {
870     fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
871         if self.is_empty() {
872             return Some(String::new());
873         }
874         let mut result = String::with_capacity(128);
875         let indent = shape.indent.to_string(context.config);
876
877         let mut derive_args = Vec::new();
878
879         let mut iter = self.iter().enumerate().peekable();
880         let mut insert_new_line = true;
881         let mut is_prev_sugared_doc = false;
882         while let Some((i, a)) = iter.next() {
883             let a_str = try_opt!(a.rewrite(context, shape));
884
885             // Write comments and blank lines between attributes.
886             if i > 0 {
887                 let comment = context.snippet(mk_sp(self[i - 1].span.hi(), a.span.lo()));
888                 // This particular horror show is to preserve line breaks in between doc
889                 // comments. An alternative would be to force such line breaks to start
890                 // with the usual doc comment token.
891                 let (multi_line_before, multi_line_after) = if a.is_sugared_doc ||
892                     is_prev_sugared_doc
893                 {
894                     // Look at before and after comment and see if there are any empty lines.
895                     let comment_begin = comment.chars().position(|c| c == '/');
896                     let len = comment_begin.unwrap_or_else(|| comment.len());
897                     let mlb = comment.chars().take(len).filter(|c| *c == '\n').count() > 1;
898                     let mla = if comment_begin.is_none() {
899                         mlb
900                     } else {
901                         let comment_end = comment.chars().rev().position(|c| !c.is_whitespace());
902                         let len = comment_end.unwrap();
903                         comment
904                             .chars()
905                             .rev()
906                             .take(len)
907                             .filter(|c| *c == '\n')
908                             .count() > 1
909                     };
910                     (mlb, mla)
911                 } else {
912                     (false, false)
913                 };
914
915                 let comment = try_opt!(recover_missing_comment_in_span(
916                     mk_sp(self[i - 1].span.hi(), a.span.lo()),
917                     shape.with_max_width(context.config),
918                     context,
919                     0,
920                 ));
921
922                 if !comment.is_empty() {
923                     if multi_line_before {
924                         result.push('\n');
925                     }
926                     result.push_str(&comment);
927                     result.push('\n');
928                     if multi_line_after {
929                         result.push('\n')
930                     }
931                 } else if insert_new_line {
932                     result.push('\n');
933                     if multi_line_after {
934                         result.push('\n')
935                     }
936                 }
937
938                 if derive_args.is_empty() {
939                     result.push_str(&indent);
940                 }
941
942                 insert_new_line = true;
943             }
944
945             // Write the attribute itself.
946             if context.config.merge_derives() {
947                 // If the attribute is `#[derive(...)]`, take the arguments.
948                 if let Some(mut args) = get_derive_args(context, a) {
949                     derive_args.append(&mut args);
950                     match iter.peek() {
951                         // If the next attribute is `#[derive(...)]` as well, skip rewriting.
952                         Some(&(_, next_attr)) if is_derive(next_attr) => insert_new_line = false,
953                         // If not, rewrite the merged derives.
954                         _ => {
955                             result.push_str(&try_opt!(format_derive(context, &derive_args, shape)));
956                             derive_args.clear();
957                         }
958                     }
959                 } else {
960                     result.push_str(&a_str);
961                 }
962             } else {
963                 result.push_str(&a_str);
964             }
965
966             is_prev_sugared_doc = a.is_sugared_doc;
967         }
968         Some(result)
969     }
970 }
971
972 // Format `#[derive(..)]`, using visual indent & mixed style when we need to go multiline.
973 fn format_derive(context: &RewriteContext, derive_args: &[String], shape: Shape) -> Option<String> {
974     let mut result = String::with_capacity(128);
975     result.push_str("#[derive(");
976     // 11 = `#[derive()]`
977     let initial_budget = try_opt!(shape.width.checked_sub(11));
978     let mut budget = initial_budget;
979     let num = derive_args.len();
980     for (i, a) in derive_args.iter().enumerate() {
981         // 2 = `, ` or `)]`
982         let width = a.len() + 2;
983         if width > budget {
984             if i > 0 {
985                 // Remove trailing whitespace.
986                 result.pop();
987             }
988             result.push('\n');
989             // 9 = `#[derive(`
990             result.push_str(&(shape.indent + 9).to_string(context.config));
991             budget = initial_budget;
992         } else {
993             budget = budget.checked_sub(width).unwrap_or(0);
994         }
995         result.push_str(a);
996         if i != num - 1 {
997             result.push_str(", ")
998         }
999     }
1000     result.push_str(")]");
1001     Some(result)
1002 }
1003
1004 fn is_derive(attr: &ast::Attribute) -> bool {
1005     match attr.meta() {
1006         Some(meta_item) => match meta_item.node {
1007             ast::MetaItemKind::List(..) => meta_item.name.as_str() == "derive",
1008             _ => false,
1009         },
1010         _ => false,
1011     }
1012 }
1013
1014 /// Returns the arguments of `#[derive(...)]`.
1015 fn get_derive_args(context: &RewriteContext, attr: &ast::Attribute) -> Option<Vec<String>> {
1016     attr.meta().and_then(|meta_item| match meta_item.node {
1017         ast::MetaItemKind::List(ref args) if meta_item.name.as_str() == "derive" => {
1018             // Every argument of `derive` should be `NestedMetaItemKind::Literal`.
1019             Some(
1020                 args.iter()
1021                     .map(|a| context.snippet(a.span))
1022                     .collect::<Vec<_>>(),
1023             )
1024         }
1025         _ => None,
1026     })
1027 }
1028
1029 // Rewrite `extern crate foo;` WITHOUT attributes.
1030 pub fn rewrite_extern_crate(context: &RewriteContext, item: &ast::Item) -> Option<String> {
1031     assert!(is_extern_crate(item));
1032     let new_str = context.snippet(item.span);
1033     Some(if contains_comment(&new_str) {
1034         new_str
1035     } else {
1036         let no_whitespace = &new_str.split_whitespace().collect::<Vec<&str>>().join(" ");
1037         String::from(&*Regex::new(r"\s;").unwrap().replace(no_whitespace, ";"))
1038     })
1039 }